Updating Page Sentry for APEX 4.0

Hi there,
I've found this forum and the regular posters and mods to be extremely helpful, so this post is more of a give-back, I hope. We recently upgraded a dev system of apex 3.0.x to 4.0.2 and ran into some issues where our use of some Page Sentry auth code that's been floating around a long time became a problem for us in terms of properly managing session state, URL forwarding, and acceptance of URL parameters for setting the value of page items in session.
I've posted [an article on our blog|http://zetetic.net/blog/2010/12/10/updating-page-sentry-for-apex-4-0-upgrade/] explaining the whole thing in what is probably very boring detail over here, and if you're so inclined, we'd love your feedback. But just so there's no need to go anywhere, here's the page sentry we ended up implementing, which makes some pretty significant mods to the old page sentry to account for what we perceived as changes in APEX's behavior somewhere in our move from 3.0 all the way up to 4.0.2.
You can view the following function nicely formatted [at this gist|https://gist.github.com/736369] :
<pre><code>
CREATE OR REPLACE function PASSPORT.oamPageSentry ( p_apex_user in varchar2 default 'APEX_PUBLIC_USER' )
return boolean
as
l_cgi_var_name varchar2(100) := 'REMOTE_USER';
l_authenticated_username varchar2(256) := upper(owa_util.get_cgi_env(l_cgi_var_name));
l_current_sid number;
l_url_sid varchar2(4000);
l_url varchar2(4000);
l_app_page varchar2(4000);
begin
-- check to ensure that we are running as the correct database user
if user != upper(p_apex_user) then
return false;
end if;
if l_authenticated_username is null then
return false;
end if;
l_current_sid := apex_custom_auth.get_session_id_from_cookie;
l_url := wwv_flow_utilities.url_decode2(owa_util.get_cgi_env('QUERY_STRING'));
wwv_flow.debug('oamPageSentry: request from ' || l_authenticated_username || ' (sid=' || l_current_sid || ') for ' || l_url);
-- split on zero or more non-colon characters, and extract the URL session ID if it is present
l_url_sid := REGEXP_SUBSTR(l_url, '[^:]*', 1, 5);
wwv_flow.debug('oamPageSentry: extracted current sid from url as ' || l_url_sid);
-- the post_login call at the end of this function will blindly append the session ID to the URL, even if it is
-- a deep link. Detect this condition, strip the duplicate session identifier, and redirect.
if REGEXP_SUBSTR(l_url, '^.*:' || l_current_sid || ':.+:' || l_current_sid || '$') IS NOT NULL then
l_url := REGEXP_REPLACE(l_url, ':' || l_current_sid || '$', '');
wwv_flow.debug('oamPageSentry: identified duplicate session id on URL, stripping and redirecting to ' || l_url);
owa_util.redirect_url('f?'|| l_url);
return false;
end if;
-- apex 4.0 appears to have problems setting session variables (possibly due to new session validation)
-- if the Session identifier present in the URL does not agree with the session identifier in the cookie
-- detect this condition, and replace the invalid URL session identifier in the URL with the valid
-- ID in from the cookie and redirect to the fixed URL
if owa_util.get_cgi_env('REQUEST_METHOD') = 'GET' AND l_current_sid <> TO_NUMBER(l_url_sid) then
l_url := REGEXP_REPLACE(l_url, '^(p=.+?:.+?):\d*(.*)$', '\1:' || l_current_sid || '\2');
wwv_flow.debug('oamPageSentry: current sid ' ||l_current_sid || ' is diferent from url sid ' || l_url_sid || ', redirecting to url' || l_url);
owa_util.redirect_url('f?'|| l_url);
return false;
end if;
-- 1. If the session is valid and the usernames match then allow the request
-- 2. If the session is valide but the usernames do not match, there may be session tampering going on. log the session out
-- 3. If the session id is not valid, generate a new session, and register it with apex
if apex_custom_auth.is_session_valid then
apex_application.g_instance := l_current_sid;
wwv_flow.debug('oamPageSentry: current sid ' || l_current_sid || ' with username ' || apex_custom_auth.get_username || ' is valid');
if l_authenticated_username = apex_custom_auth.get_username then
wwv_flow.debug('oamPageSentry: current session username ' || apex_custom_auth.get_username || ' equal to header username ' || l_authenticated_username);
apex_custom_auth.define_user_session(
p_user=>l_authenticated_username,
p_session_id=>l_current_sid);
return true;
else
wwv_flow.debug('oamPageSentry: username ' || apex_custom_auth.get_username || ' mismatch with ' || l_authenticated_username || ' loggout');
apex_custom_auth.logout(
p_this_app=>v('APP_ID'),
p_next_app_page_sess=>v('APP_ID')||':'||nvl(v('APP_PAGE_ID'),0)||':'||l_current_sid);
apex_application.g_unrecoverable_error := true; -- tell apex engine to quit
return false;
end if;
else -- application session cookie not valid; we need a new apex session
wwv_flow.debug('oamPageSentry: current session ' || l_current_sid || ' is not valid');
l_current_sid := apex_custom_auth.get_next_session_id;
wwv_flow.debug('oamPageSentry: generated new session id ' || l_current_sid);
apex_custom_auth.define_user_session(
p_user=>l_authenticated_username,
p_session_id=> l_current_sid );
apex_application.g_unrecoverable_error := true; -- tell apex engine to quit
if owa_util.get_cgi_env('REQUEST_METHOD') = 'GET' then
wwv_flow.debug('oamPageSentry: GET request, remembering deep link ' || l_url);
wwv_flow_custom_auth.remember_deep_link(p_url => 'f?'|| l_url );
else
l_url := 'f?p='||
to_char(apex_application.g_flow_id)||':'||
to_char(nvl(apex_application.g_flow_step_id,0))||':'||
to_char(apex_application.g_instance);
wwv_flow.debug('oamPageSentry: POST request, remembering deep link ' || l_url);
wwv_flow_custom_auth.remember_deep_link(p_url=> l_url );
end if;
-- in previous versions of apex the remember_deep_link call would actually work and cause
-- post_login to redirect to the target URL. This doesnt work any more in 4.0. Instead,
-- we'll pass the target page in to the post_login call directly. Post login will blindly
-- append the session ID to the end of p_app_page when it redirects, but we
-- clean that up with the first cleanup redirect at the beginning of the function
l_app_page := SUBSTR(l_url, 3, LENGTH(l_url) - 2);
wwv_flow.debug('oamPageSentry: post_login for ' || l_authenticated_username || ' app_page ' || l_app_page );
apex_custom_auth.post_login(
p_uname => l_authenticated_username,
p_session_id => nv('APP_SESSION'),
p_app_page => l_app_page
return false;
end if;
end oamPageSentry;
</code></pre>

Billy,
Thanks a lot for this great info. It seems to have solved the problem I have been having with an NTLM page sentry function for the last 2 or 3 days. Very difficult stuff to debug what is going on inside these functions when they (obviously) behave differently once you have logged in etc.
As I said, your solution seems to solve my problem - but I have a couple of questions :
1. Is this related to 10347091 which is mentioned on http://www.oracle.com/technetwork/developer-tools/apex/downloads/apex402knownissues-189793.html ?
If yes, did you try the patch?
2. Have you logged a bug or had any feedback (externally or within the forum) from Oracle people on this issue?
I was about to log a bug regarding the deep linking and FSP_AFTER_LOGIN_URL not behaving correctly when I noticed the known issues and now your valuable work. I was going to try the patch, but I'd rather not apply it unless I know it will solve my problem.
Please let me know.
Thanks
Glen

Similar Messages

  • I am unable to update Pages through the App store. It appears someone else used my machine to update Pages previously.For the update,  I am asked to login to another account. I want to remove it and download the latest version. Can I do this?

    I was notified that Pages and one other application have updates. When I tried to login, the Apps page asks me to login to an account I am not familiar with. I imagine that this machine was used by someone else to update or download 2 apps. I would like to delete them and download the latest apps using my account. Can I do this?

    You can log in to your own account at any time and access the appliactions you have paid for, just fill in your own Apple ID.
    Peter

  • Page Sentry Setup - Oracle ApEx 4.1.1

    Hi,
    Hoping someone can please assist or point me in the right direction but I am creating a new Page Sentry setup in Oracle ApEx 4 based on the following link:
    Updating Page Sentry for APEX 4.0 Upgrade:
    http://zetetic.net/blog/2010/12/10/updating-page-sentry-for-apex-40-upgrade.html
    I have pretty much just used what has been provided from this link with a few minor changes but my question that I cannot figure out why it is happening is that in MS IE8, when I fire up my app, it all starts up fine and and sets up my APP_USER and APP_SESSION fine but what I have found that, when I go to a new tab in IE8 and start up another session for this very same APP_ID, it is assigning the same APP_SESSION as the previous session, which is not what I am after from the Page Sentry.
    Could someone please assist with what I need to change this pl/sql code, so that a new session id is always generated when starting up a new IE8 tab in the same browser window.
    Thanks.
    Tony.

    Hi Tony,
    If you are using OAM, why would you want each tab to have a different session?
    (OTN PDF on OAM - http://www.oracle.com/technetwork/developer-tools/apex/learnmore/apex-oam-integration-1375333.pdf )
    If I'm not mistaken . . . your problem with Apex 3 is now solved in Apex 4 (unless you implemented a broken page sentry function), because the session id in the url is backed by a cookie. If the session id in the url doesn't match the credentials in the cookie, it cancels the session.
    At the same time, since browsers share cookies between different tabs, the database can't distinguish between page calls from one tab from page calls from another tab - because they pass the same cookie. This is a browser issue, not an apex limitation. I don't think its possible to have each tab be treated as a separate session, unless you disable the cookie, and that leaves you open to the problems you had with Apex 3.
    I keep saying "if I'm not mistaken", and that's because I've got 0 applications under my belt (well, I have a websheet, but that doesn't count). I've been studying page sentry functions on-and-off since November 2011, and got nowhere. Fortunately, I discovered how to do what I wanted to do without using a page sentry function.
    Here are the most helpful links I found - most of the code are variations on a theme, and they reference functions that are not well documented.
    I found Christian Neumueller's second post on this page very enlightening - Re: APEX 4.1 Login page kills existing session cookie
    I might implement this - http://www.oracle.com/technetwork/issue-archive/2009/09-may/o39security-101079.html
    You've probably seen Joel Kallman's post - http://joelkallman.blogspot.co.uk/2010/10/custom-authentication-scheme-for-oracle.html
    Here's one about NTLM, which isn't what you're looking for, but its another example of a page sentry function - http://jastraub.blogspot.co.uk/2008_03_01_archive.html
    There is a little bit about session management and the login process in this pdf - http://www.sumneva.com/c/sumneva/presentations/APEX%20Behind%20the%20Scenes.pdf - all of which is very interesting.
    Tak

  • Apex Custom Authentication Schema Page Sentry Error

    Hi,
    I am using Application Express 4.0.0. I am struggling with a strange problem while trying to implement custom authentication schema.
    I declare a page sentry function 'page_sentry' which returns TRUE or FALSE based on certain conditions. My page_sentry is as follows:
    FUNCTION PAGE_SENTRY RETURN BOOLEAN
    IS
    l_username VARCHAR2(512);
    l_session_id NUMBER;
    BEGIN
    IF USER != 'APEX_PUBLIC_USER' THEN
    RETURN false;
    END IF;
    l_session_id := wwv_flow_custom_auth_std.get_session_id_from_cookie;
    -- check application session cookie.
    IF wwv_flow_custom_auth_std.is_session_valid THEN
    apex_application.g_instance := l_session_id;
    l_username := wwv_flow_custom_auth_std.get_username;
    wwv_flow_custom_auth.define_user_session(
    p_user => l_username, p_session_id => l_session_id);
    RETURN true;
    ELSE
    --redirect to login page using OWA_UTIL.REDIRECT_URL
    END IF;
    RETURN false;
    END page_sentry;
    And Cookie Name : _AUTH
    At first, It always returned FALSE. I wasn't getting any error. However, even after forcibly returning TRUE from the page_sentry function the redirect was still not happening. I tried to look into what cookies were being set for the same and I found this:
    Name:     ApexLibErrorStack1
    Content:     page%3D1%3Cbr%20%2F%3EERR-1201%20session%20ID%20not%20set%20on%20custom%20authentication
    I couldn't find any relevant help for this. Worst of it is I cannot set any cookie from the page_sentry function at all. Please help!

    Hi all.
    Can someone please help me out with the above issue. I am not sure if things are wrong at my end or is this an apex bug.
    --Update:
    The source of my problem perhaps lies in the manner in which I have configured my Oracle HTTP Server. When I disable port HTTP server on port 80 and run apex without it on default port 8080, the custom authentication schema cookie gets set.
    Executing the following with Oracle HTTP Server:
    OWA_UTIL.PRINT_CGI_ENV;
    gives:
    HTTP_COOKIE = ApexLibErrorStack1=page%3D1%3Cbr%20%2F%3EERR-1201%20session%20ID%20not%20set%20on%20custom%20authentication.; ORA_WWV_R1=%23ALL; ORA_WWV_R2=%23ALL; ORA_WWV_R3=%23ALL
    And without HTTP Server:
    gives:
    HTTP_COOKIE = WWV_CUSTOM-F_1420403886791332_100=9625AAC49B9951D8;......
    Did I miss something in my HTTP server configuration ?
    Edited by: pc on Jan 2, 2012 3:15 AM

  • Custom pagination for APEX 4.2 interactive report using Page Zero

    Hi,
    I want to implement an «Custom pagination for APEX 4.2 interactive report» using a «page zero».
    I recently migrate from Apex 3.1 to Apex 4.2 and my «Custom pagination for APEX 3.1 interactive report» using a «page zero»  is not working any more.
    So now I try to adapt an excellent example of Jari Laine for 4.0 but using a page zero.
    I put the code JavaScript to Page zero but I must create an dynamic action to fire only for an interactive report region.
    It’s a good idea?
    Thank you

    Thought I would try once more with my DatePicker question.
    On the Apex.Oracle.Com website I have created a 1 page application that has an Interactive Report.
    [url http://apex.oracle.com/pls/apex/f?p=15655:1]
    user = 'test'
    password = 'test'
    I have 2 questions :
    (1) In IE7, press 'Actions', 'Filter'. On the Column dropdown list, select 'Order Timestamp'.
    Notice the prompt icon to the right of the 'expression'. This should change to the Datepicker, but in IE7 it does not. Try the samething in Firefox or Chrome and the Datepicker will appear.
    Is this a BUG, or does Apex 4.02 not support IE7 ?
    (2) In Firefox or Chrome, where you can now see the Datepicker, you will notice that it is the new style picker, not the old style ( called 'classic' ). I want to change it so that it shows the 'classic' datepicker not the new, but cannot see how to do it, if indeed you actually can.
    I would really appreciate it if someone could take a look and let me know if I am going mad, or if we need to get all our users onto IE8. We have now gone live with Apex 4.02 and need to resolve these issues.
    Thanks in advance.
    Edited by: DooRon on 10-Mar-2011 05:13

  • Creating process for multiple Date fields for update or insert in APEX

    hello there,
    could someone please help me?
    i have a form on Apex based on view that is based on three tables and updating and inserting ok using trigger instead of.
    i have a problem now as in my form i have around 75 fileds (items) incuding 30 or more date fields which could be populated or left blank or update later.
    so for each date field i have two boxs; one for date, input as dd/mm/yyyy (text field) and second for time, input as 23:45. All dates will be insert or update manually by user. so as i mentioned not all date fields could be poulated at one stage.
    so i have created some process and validations and all of them work fine but i came accross if date left blank then (:) giving me problem so i have done following further process for each date field. In real table all the date fields have data type date.
    declare
    v_my_var date; -- for first date field
    str_dy VARCHAR2(10);
    dt_indx date;
    str_tm VARCHAR2(20);
    tm_indx date;
    begin
    str_dy := :p4_first_date
    str_tm := str_dy||' '||substr(:p8_first_date_hh,1,2)||':'||substr(:p8_first_date_HH,4,2);
    dt_indx := to_date(str_tm,'DD/MM/YYYY HH24:MI');
    IF str_dy is not null then
    v_my_var :=dt_indx;
    ELSE
    v_my_var := NULL;
    END IF;
    update table 1 set my_date = v_my_var where d_id= :p4_d_id;
    end;
    above code work fine but one date field of course therefore i have to do same code for each date field with changes and initialise variable again and again for each field.
    so i like to ask is there any easy way that is more professional. i was thinking about the procedure and using collection or similar but honestly not much experience on that so could some one please help me?
    I will be very thankful.
    KRgds

    Hi,
    You can do the needful by re-using the code if you can give the item names as P8_DATE1, P8_DATE_hh1, P8_DATE2, P8_DATEhh2 etc..So your item name just differs by a sequence.
    Now you write function which will return desired date value taking above items as input. Pass item names to this function, get session state using APEX_UTIL.GET_SESSION_STATE('item_name') API.
    Now modify you code as
    FOR i IN 1..30
    LOOP
    v_date_array[i] = f_get_date('P8_DATE'||i, 'P8_DATEhh'||i);
    END LOOP;
    ....Now you have all date valus in array. Just write one update as follows
    UPDATE  TABLE1
    SET date1 = my_date_array[1], date2 = my_date_array[2]..
    WHERE ....Hope it helps :)
    Cheers,
    Hari

  • I plugged my iPhone 4s into my macbook air and it asked me if I wanted to update to the latest iOS update and when I did, it stayed on the black update page for a few hours now and it is not turning on. How do I get my phone to restore?

    After plugging my phone into my computer, it asked me to update and then got stuck on the update page and the bar hasn't moved at all.
    Then I plugged it into my computer after it died just like it told me to and it asked me if i wanted to restore my phone. i pressed 'okay' and it is stuck on the black page again for a few hours without the bar moving at all and my phone is completely useless.
    How can I get my phone to update and restore?

    If you're in recovery mode, all data is ALREADY lost.  You've no choice but to restore.

  • What is the Firefox start page looking for when it tells me I'm not using the most current version and need to update Firefox? The check for updates button says my version is up to date, but the start page disagrees. Is there a setting I need to adjust?

    Okay: My start page is the one Google provides: http://www.google.ca/firefox?client=firefox-a&rls=org.mozilla:en-US:official and frequently it has a note underneath the search box that "You're not on the latest version of Firefox. Upgrade today to get the best of the Web!" But when I check for updates on the About Firefox window, it says my Firefox is up to date. So what is the start page looking for? Is there some setting I need to adjust? Or is this a Google marketing ploy to get me to switch to Chrome?

    I tentatively think I have the solution; this is for when the session id value 'thing', in about:config, referred to in other answers is already on the default/rest state.
    I am assuming, I don't what to imply certainty in-case there is a value wrong in our browsers, that the problem stems from the fact that our homepage is no longer the default for Firefox.
    So, go to 'Options'>'General' and delete the homepage. This will reset it to the new address, which is 'about:home'.
    I hope this helps some people.

  • Using a LOV in an Update page for a QBE Report

    In my QBE Report-based portlet, I have set one of the fields to use a LOV.
    When I go into Customize for the portlet, that field uses the LOV, but when I click on the Update link for a row, the same field does NOT use the LOV.
    How do I make it use the LOV in the Update page?
    Thanks for any help!
    Regards
    Bryan

    Folks --
    Is this question a difficult one?
    I'd appreciate even a pointer...
    Thanks
    Bryan

  • InDesign "Save As" dialog box for every operation in INDB book file; updating page numbers, etc.

    So I haven't used INDB files in awhile in InDesign, and today was the first time I attempted to adjust an old INDB (created originally with CS5).  For every operation of adjusting the book file, such as updating page/section number or adding/removing an INDD file I get a Save As dialog box for each constiuent INDD file.  This is quite annoying since the book has lots of INDDs, and I have to click through the tedious save as with each.  I previously worked with INDBs in CS5, and never encountered this, there was no need to save each INDD in the book just to update the page numbers.  I tried creating a new INDB in CS5.5 thinking that maybe it was because I was working from a file created with an older version of InDesign.  Does anyone know if there is a way to suppress the Save As dialog?  Another thing I noticed that is supremely frustrating is that this new build of InDesign doesn't default to saving the file in its original location, and I have unwittingly ended up saving files in other places, only to later realize it.  I am using InDesign CS5.5 Ver. 7.5.2 on Mac OSX 10.7.3  Any help is greatly appreciated. 

    It will only happen once (and frankly, what you are doing is a very bad idea).
    When you open the old book in CS5, you are converting to the new format. All of the legacy files also need to be converted, even if added to a new book in CS5.5, hence the Save As since ID will not overwrite the old versions until you tell it to (and that's the bad idea). I strongly recommend (though it's too late for any books you've already updated and overwritten) that you either continue to edit in the old version, or that you take the time to open each file in the original version if it's older than CS5, then export to interchange format (.inx or .idml, depending on version), open those files in CS5.5, save with new names so you don't overwrite anything, then add to a new book and proceed.
    One of the reasons I recommend this seemingly time-consuming approach is that there are too many reports (another one yesterday) of some sort of problem that pops up late in the editing cycle when converting legacy files to CS5/5.5 by opening them directly and saving. This is a belt and suspenders insurance approach, and is far less labor intensive than starting over, which might end up being your other option down the road if something goes wrong.

  • Apple id not valid for updating Pages

    Hi all, I just got a mac mini. I used time machine to migrate my desktop from my 2008 mac book. I thought I was using the same apple id for everything (I only have 1), but when I go to update pages it says the apple id does not match. What is happening here?

    OK, I just got off the phone with customer support and my problem is fixed:
    Go to the app store where the update is, go to the search button top right type in "pages" it'll come back with about 300 options. Pages should be the first one, click on "accept" (below the little pages icon), you'll have to put in your appleID/password again and it'll actually go through and re-associate all the standard OSX apps - imovie, iphoto etc.
    Once this is done they should all appear in the "purchased" tab and you can update pages.
    Thanks to CJ at customer support!

  • How do I get to updates page for books on iPad

    I apparently have 51 books needing updated on my iPad Air. How do I get to my updates page please?

    Hi Liz,
    You must be noticing the updates badge next to the Purchased button in the navigation bar.  If you click on purchased, you can scan the items for the ones that have an update.
    About iBooks book updates - Apple Support
    I'm not seeing a filter for "updates only" however.
    Best regards,
    Nubz

  • After purchasing the Photoshop cs6 update for my Mac, the download page says for "Windows". Will this still work? How do I get the right download without purchasing again

    After purchasing the Photoshop cs6 update for my Mac, the download page says for "Windows". Will this still work? How do I get the right download without purchasing again?

    no.
    you need the update for a mac, not windows.
    Product updates

  • Ap Store shows update for DropCopy and lists it as installed yet it will no clear my updates page in my account.

    Ap Store shows update for DropCopy and lists it as installed yet it will not clear the updates page in my account. How do I clear this?

    I tried the debug mode in mac app store too >> Fail.
    The first time I feel disapointing from apple software.

  • I have a website for my artwork.  I had a PC and. Now an iMAC.  I've used Dreamweaver to update the website.  I used to go to Filezilla on the PC a upload the updated pages which would then show updated on the internet.  What file like FileZilla should I

    I have a website for my artwork.  I had a PC and. Now an iMAC.  I've used Dreamweaver to update the website.  I used to go to Filezilla on the PC a upload the updated pages which would then show updated on the internet.  What file like FileZilla should I use?

    You can download it here:
    https://filezilla-project.org/download.php?show_all=1
    Click the Source Forge link - make sure you reject offers for any other software during the installation, Source Forge usually will ask you to install other stuff in addition to what you want (Filezilla).
    It works well on Mac.
    Dreamweaver also has built-in FTP functionality if you want to check that out.

Maybe you are looking for

  • How do I transfer my iTunes music from one internal hard drive to another internal hard drive without having to locate every song?

    So I have two internal hard drives in my laptop but the hard drive that my music is on is running out of space. I want to transfer my music from the smaller hard drive to the larger hard drive so my computer can run faster. If I do this I'm afraid I'

  • Sending image to my big screen TV with Winows running on my MacBook

    Greetings from RJ - Brazil! I'm a newcomer to the MacBook and I'm having trouble sending my desktop image to my big screen TV when running Windows (Bootcamp partition). With MacOSX there is no problem at all, just connecting the DVI -HDMI cable and s

  • Start up error messages

    i have a new mac pro....transfered all my bits from my air ( as per instructions on the webb apple webb site) but when i start up the mac  i get an error message saying that a mobole device has had to be stopped as it is unstanble ......any one got a

  • Automate face finding & cropping

    Hi, I am looking for a software or plug-in for Photoshop which can automatically find face on photo and then crop it (if cropping of the face is not perfect it is ok) or make a portrait. Then save result which can be used by Photoshop scripts. There

  • Intermittant USB port

    When I purchased a docking station, I noticed that if  moved it, the USB would ding down/up.  I thought it was the station, but it turns out that it is the USB port on the Carbon.  I tried a different docking station and USB cable.  The only solution