Solved

Delphi - DropBox

Posted on 2012-03-21
22
3,788 Views
Last Modified: 2012-06-27
hello,
I state that I have activated everything you need on site dropbox.

Is there any example of how to upload and download files using delphi?

thanks
0
Comment
Question by:danz67
  • 12
  • 9
22 Comments
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37749540
I have a fully working Dropbox.pas unit that I wrote for my company that I'm using for my current project. Unfortunately I cannot provide it to you because it is owned by my company, but I can assist you if you have specific questions. I will outline the basics of my interface to get you started.

1. I use TIdHTTP to make the HTTP requests into their REST web service.
2. I use the open source OAuth.pas (Google it) for the OAuth; however, it has two major flaws. If you choose to go this route of writing your own interface and using OAuth.pas I can outline the bugs and what I had to do to correct them.
3. I use SuperObject JSON parser (Google it) to process the JSON results. I started my project under Delphi 2009 and am now on XE2. I understand that XE2 has JSON support, but I've stuck with SuperObject since it works well and I am familiar with it.

Those are the basic components that make up my application. You need to understand OAuth. The latest version of their API requires OAuth authentication including obtaining the request token, authorizing the user, and finally getting the access token. This is a critical step and there is a lot out there on the subject. You will find that many others will direct you to OAuth.net and tell you to read the info there. This is almost useless. It's like telling someone to read the entire manufacturer's service manual for their car when they ask how to change the oil. It's a much more complicated standard than what you really need to know for real world application, especially for communicating with Dropbox. Search OAuth and find some sites that outline the basic principles, then start playing with the API. Their dev forum is pretty good too.

Your HTTP requests must be formatted in a certain way to include both the OAuth parameters as well as any required for the particular API method you are using. The parameters, including the OAuth ones, are to be alpha sorted. This is one of the flaws of the OAuth.pas project. It does not alpha sort, it is easily corrected. Here is a sample call to get the metadata of a folder named test.

3/21/2012 2:39:28 PM

    GET: /1/metadata/sandbox/test?file_limit=10000&list=true&include_deleted=false&oauth_consumer_key=********************&oauth_nonce=07DE9EE58FE86DB711A77B8675E7183C&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1332358768&oauth_token=********************&oauth_version=1.0&oauth_signature=RTDYiCRIsXrB5yN%2Bkzs%2FMZX2KGo%3D

    Parameters:

    file_limit=10000
    list=true
    include_deleted=false
    oauth_consumer_key=********************
    oauth_nonce=07DE9EE58FE86DB711A77B8675E7183C
    oauth_signature_method=HMAC-SHA1
    oauth_timestamp=1332358768
    oauth_token=********************
    oauth_version=1.0
    oauth_signature=RTDYiCRIsXrB5yN%2Bkzs%2FMZX2KGo%3D

Open in new window


I know this sounds largely vague, but if you understand HTTP requests and TIdHTTP and then understand OAuth then making calls to the RESTful web service is a piece of cake.
0
 

Author Comment

by:danz67
ID: 37749931
I have find this article http://www.delphipraxis.net/161667-oauth.html
but when run the project and put parametre for access i get this error

what wrong?
Schermata-03-2456008-alle-22.01..png
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37750166
That other post uses version 0 of the API. They are on version 1 now, things are a little different. Review the documentation of the latest version for reference. In most cases it simply means change the "0" from the URLs to a "1" but in some cases there are other differences in the URL. I would start your application using version 1 in case things change. The request_token, authorize, and access_token methods should be the same, but the 1 needs to be in the URL.

The base URLs look like...

https://api.dropbox.com/1/oauth/request_token
https://www.dropbox.com/1/oauth/authorize
https://api.dropbox.com/1/oauth/access_token

The sample code also does not use HTTPS. Not a huge deal, but if you are using TIdHTTP you can use HTTPS if you download OpenSSL library. First, get your stuff working and then Google TIdHTTP and SSL and set it up for SSL access.

That sample code looks good. It does things slightly different than how I did it, but it should still be OK. I setup my TOAuthRequest like this for example...

// When requesting the token...
  OAuthRequest := TOAuthRequest.Create('');
  HMAC := SignatureMethodClass.Create;
  try
    { Generate and sign the request }
    OAuthRequest.HTTPURL :=  'https://api.dropbox.com/1/oauth/request_token';    OAuthRequest.FromConsumerAndToken(FOAuthConsumer, nil, '');
    OAuthRequest.Sign_Request(HMAC, FOAuthConsumer, nil);

Open in new window


It's a minor difference and probably has no impact.

So, why are you getting 401? Do you have a an API token/secret pair from Dropbox? If not, you need to obtain one first. If you do then chances are good that your app is not yet published. This means you can only access your own account. That is, the account that owns the API token/secret pair you are using. Until your app is published this is how it is. If you are doing these things then trap the return message from Dropbox. Wrap your HTTP.Get calls in a try except.

try
  http.get(URL);
except
    { Log exceptions and re-raise }
    on E: EIdHTTPProtocolException do
    begin
      ShowMessage(E.Message);
      Raise;
    end;

Open in new window


E.Message above contains a message in JSON format that will be more helpful. Grab that and post it here. ***BUT*** That message WILL contain your super secret API token/secret pair. Anywhere in there that you see oauth_token=<something> or oauth_token_secret=<something> then replace <something> with * or similar to mask that before sharing. If you post it I'll look at it. That will help.
0
 

Author Comment

by:danz67
ID: 37751240
nothing to do, I always error. I hope you will give me a complete and ready example that works with Delphi 7.
Thanks for your time, hello
0
 
LVL 19

Expert Comment

by:Thommy
ID: 37751358
Check following links...

Units/Tools to communicate with Dropbox using Delphi...
DropboxDelphi / Dropbox.pas

How to upload files with the Dropbox REST API

Dropbox api for Delphi?
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37752490
nothing to do, I always error.

I do not understand. My previous reply included code to retrieve the error message. Your 401 error is accompanied by a very useful error message that will describe your problem. Somewhere in your code you are using an HTTP component, presumably TIdHTTP, to send a request to Dropbox. Wrap all of your "get" calls in a try..except and handle the EIdHTTPProtocolException exception. E.ErrorMessage will contain the full error message and this will help. I mistated E.Message in my first reply, but E.ErrorMessage is what you are after.

For example, I have modified my application to use API key that does not yet have production status from Dropbox. I then tried to login to another user's account. In both error messages below an HTTP 401 error was raised, but look at the error message that was returned. It tells me exactly what the error condition was! Please do this and send the message. The post you sent earlier should work fine, but again, it's version 0 of the API. At some point they will discontinue support for this. Modify that code to use version 1 of the API. The URLs have the following substring... "/0/" in them. Change those to "/1/"

 {
        "error": "Access token not found."
    }

Open in new window


{
        "error": "Only a limited set of users can receive access tokens while this app is in development mode"
    }

Open in new window

0
 

Author Comment

by:danz67
ID: 37756203
"MichaelStaszewski"

forgive my inability to do what you asked me, I hope you can prove the project I have led Example DropBox Delphi and modify it to do it function properly, including file uploads, as I wrote in my initial request.
I know I ask too much but unfortunately I can not, hello and thanks
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37756771
The sample app you sent should work for authentication, even if it does use version 0 of the API. I will confirm when I am in the office later. It is nearly identical to my project which works. If you are getting 401 errors during authentication the we must figure out why. I do not understand why you cannot trap the exception and review its message. This is standard debugging practice for any application.
0
 
LVL 4

Accepted Solution

by:
MichaelStaszewski earned 500 total points
ID: 37757658
I am on XE2 so I do not know if this will compile under D7, but try the following code. Use the attached unit in your project. Add it to your form's uses list and add two buttons and implement the OnCreate and OnDestroy handlers. Set the code in there as follows. Does your app have application folder access only or full access to Dropbox? If full access to Dropbox then FDropbox.Root below should be set to 'dropbox' otherwise if your app has application folder access only then leave it as is.

There are particular challenges here that you will need to overcome. I have not provided everything for you. I would like to see you show some initiative in learning how to do this. I have; however, provided a class that will authorize properly using the latest version of the API. It also has skeleton functions in there for uploading and downloading, but you will encounter problems in each. The download file routine should fail for you. If I recall correctly OAuth.pas returns an invalid signature string after authentication. (Hint) I think it has extra items in it. I have wrapped the HTTP call in try except and even write out the exception to your C:\ drive for you to look at. It should tell you exactly what the problem is. Upload file should fail for the same reason as DownloadFile, but it also uses the HTTP PUT method. OAuth.pas assumes that all calls are GET and thus signs non-GET requests incorrectly. You need to modify OAuth.pas.

I will be glad to help you with any questions you have, but you must try to understand what it is you are doing because if your application is something you want to share with others you will run into more issues with proxy servers and the like that are more complicated than simply putting a file on Dropbox.

The other sample code you have found is poorly written, uses the old API, and does not use HTTPS. This code uses HTTPS and in order for it to work you must download OpenSSL. Google it, it's an open source project. There are 2 DLLs that you need to put into the same folder as your executable.

procedure TForm1.Button1Click(Sender: TObject);
begin
  FDropbox.AppKey := YourAppKey;
  FDropbox.AppSecret := YourAppSecret;
  FDropbox.GetRequestToken;
  FDropbox.Authorize;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  FDropbox.GetAccessToken;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDropbox := TDropbox.Create;
  FDropbox.Root := 'sandbox';
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FDropbox.Free;
end;

Open in new window

Dropbox.zip
0
 

Author Comment

by:danz67
ID: 37758489
FDropbox where is declared?
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37758498
In your form somewhere. Just add it to the private section of your form.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37758502
I should have said, for testing put it in your form somewhere. In a real world application you're probably better off creating a singleton somewhere that is accessible to all parts of your application that may need it.
0
 

Author Comment

by:danz67
ID: 37758765
Let me explain what I did:

1. I created a new project
2. I imported the Dropbox Unit that I have provided
3. I imported OAuth
4. I put 2 buttons
5. I put the code as attached as I have explained

I can only understand this blessed FDropbox where and how it developed.
I think the last point and then we're done, to implement the various methods for uploading and downloading as I take the official documentation of DropBox, I used already in an iPhone app that I made the other day.
Excuse me for my inability, but I have little familiarity with these things, thanks
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37758835
Unless I am mistaken this is a place to ask for assistance when you get stumped. If you would like to hire me to write the code for you we can work something out. Until then if you are out of your league with Delphi you should perhaps start running through some of the demos to understand the basics of it. I'd also learn key debugging skills. It does not appear that you are familiar with exception handling and how to debug the error messages so I do not feel that you are capable of developing and maintaining the application with your current knowledge. I am more than willing to help you if you get into a bind with this. But I cannot give you my complete code that my company owns nor will I write the complete Dropbox code for you for free. It is unfortunate, but you seem unwilling to try this for yourself.

I think the last point and then we're done, to implement the various methods for uploading and downloadin

I assume that you have tried those methods in the code I sent. What error did you get? If it was an EIdHTTPProtocolException I've already handled that for you and placed the error message in a file for you. Look at the code and you will see where the error is saved to file. Assuming that you get an EIdHTTPProtocolException in DownloadFile or UploadFile it will be logged and the error message will tell you what is wrong. Be careful posting that to someone though because it will contain your API key/secret pair. You should mask those out prior to showing anyone.
0
 

Author Comment

by:danz67
ID: 37758882
Maybe I explained incorrectly (Google translate).
I have a good knowledge of Delphi, if you go on my site www.drivergest.it see what I can do. I not very familiar with Delphi but with this type of approach. I understand that you can not write code for me for free, so if it's a question of money let me know if you're willing to do this work for me. Thanks for your valuable time.
0
 

Author Comment

by:danz67
ID: 37763189
I am committed and I was able to run the code you gave me, I was very tired and could not concentrate. After pressing the button1 it opens the browser with request for access to Dropbox (or better authenticate the application), now I upload the files and I think we should use the put option, but how do I find a list of files on a grid?

Thanks for everything, hello
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37764042
I will be in office tomorrow and can help better with my pc up. I'm glad you were able to authenticate. In my experience that was the hardest part.
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37767487
> how do I find a list of files on a grid

This may have been lost in translation. I am not sure what you mean here.
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37767513
OAuth.pas is open source. I've tried unsuccessfully to contact the author with my changes, but it seems to have a few issues. To TOAuthRequest I've added a new property called Method.

    property Method: string read FMethod write FMethod; // TOAuthRequest assumes HTTP GET always. Sometimes you need other methods.

Open in new window


Also, TOAuthSignatureMethod_HMAC_SHA1does not order the parameters correctly. This is evident when using Dropbox. The parameters in the URL including the OAuth params must be in alphabetical order before signing. To accommodate this you must modify the TOAuthSignatureMethod_HMAC_SHA1class in OAuth.pas or create a subclass as I've done and write a new build_signature method. If you go the subclass route the code I previously sent should be modified to create an instance of your TOAuthSignatureMethod_HMAC_SHA1 descendant. Here is my build_signature code.

{:------------------------------------------------------------------------------
@summary The original class has 2 main bugs. First, it assumes that GET is the
         only HTTP method used and has no support for POST, DELETE, etc. Second,
         the original class would always construct the base string by placing
         URL parameters first followed by the OAuth parameters. Per section 9.1.1
         of the OAuth specifications (http://oauth.net/core/1.0/#anchor14) this
         is incorrect. The base string should be constructed such that all parameters
         including the OAuth ones are sorted in alphabetical order.
@------------------------------------------------------------------------------}
function TYourOAuthSignatureMethod_HMAC_SHA1.build_signature(Request: TOAuthRequest; Consumer: TOAuthConsumer; Token: TOAuthToken): string;
  {:----------------------------------------------------------------------------
  @summary 
  @----------------------------------------------------------------------------}
  function Base64Encode(const Input: TIdBytes): string;
  begin
    Result := TIdEncoderMIME.EncodeBytes(Input);
  end;

  {:----------------------------------------------------------------------------
  @summary 
  @----------------------------------------------------------------------------}
  function EncryptHMACSha1(Input, AKey: string): TIdBytes;
  begin
    with TIdHMACSHA1.Create do
    try
      Key := ToBytes(AKey);
      Result := HashValue(ToBytes(Input));
    finally
      Free;
    end;
  end;
var
  Parm, consec, toksec: string;
  SignableParams, Fields: TStringList;
  I: Integer;
begin
//  inherited; // <------ NO!

  Fields := TStringList.Create;
  SignableParams := TStringList.Create;
  try
    { Request.Fields is a string. We need the fields broken out into a list. }
    Fields.Delimiter := '&';
    Fields.DelimitedText := Request.Fields;

    { Params must be sorted to be OAuth compliant. See section 9.1.1 here: http://oauth.net/core/1.0/#anchor14 }
    SignableParams.Sorted := True;

    { Add all URL parameters }
    for I := 0 to Fields.Count - 1 do
      SignableParams.Add(Fields[I]);

    { Add all OAuth parameters, excluding oauth_signature }
    for I := 0 to Request.Parameters.Count - 1 do
      if Request.Parameters[I] <> 'oauth_signature' then
        SignableParams.Add(Request.Parameters[I]);

    { Build out the base string parameter }
    Parm := TOAuthUtil.urlEncodeRFC3986(Request.Scheme) +
      TOAuthUtil.urlEncodeRFC3986(Request.Host) +
      TOAuthUtil.urlEncodeRFC3986(Request.Path);

    for I := 0 to SignableParams.Count - 1 do
    begin
      if I = 0 then
        Parm := Parm + '&'
      else
        Parm := Parm + TOAuthUtil.URLEncodeRFC3986('&');

      Parm := Parm + TOAuthUtil.URLEncodeRFC3986(SignableParams[I]);
    end;

    { Set the base string }
    Request.BaseString := IfThen(Request.Method = '', 'GET', Request.Method) + '&' + Parm;

    { Generate the signature }
    if Token <> nil then
    begin
      consec := TOAuthUtil.urlEncodeRFC3986(Consumer.Secret);
      toksec := TOAuthUtil.urlEncodeRFC3986(Token.Secret);
      consec := consec + '&' + toksec;
      Result := Base64Encode(EncryptHMACSha1(Request.BaseString, consec))
    end
    else
    begin
      consec := TOAuthUtil.urlEncodeRFC3986(Consumer.Secret);
      consec := consec + '&';
      Result := Base64Encode(EncryptHMACSha1(Request.BaseString, consec));
    end;
  finally
    SignableParams.Free;
    Fields.Free;
  end;
end;

Open in new window


I am not sure what implications, if any, this will have on other projects using OAuth for other web services, but for Dropbox it works well.
0
 

Author Comment

by:danz67
ID: 37767742
> This may have been lost in translation. I am not sure what you mean here.

Let me explain. I am interested in viewing file dropbox, directly into a grid
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37767890
You would like a listing of all files that are stored in Dropbox and you would like to display them in a grid. Is this correct? Is so, use the /metadata Dropbox API method. It returns metadata for a file or folder. You can iterate the data populating any control that you want.

Read the documentation on Dropbox's site. It covers all of these things well for you. Using the class I sent you it is a fairly simple task to make the calls into the REST API and process the results. In an earlier reply I mentioned that I use SuperObject to process the JSON. You will need to process the JSON for /metadata. For upload and download files you can probably get away without it, but for /metadata you will need to process the results. Delphi 7 does not have a JSON parser so SuperObject is your best bet. Google SuperObject and you will find it. There are others, but this is the one I use and have found it to be adequate for the JSON returned by Dropbox.

Best of luck to you, but I am going to abandon this discussion. I do not see you attempting anything on your own and coming back with specific questions and full descriptions of your problems. I see only questions that look like someone is asking others for working code to do what they want. You are the developer and should be writing your code!

Keep the OAuth post above around as you might need that. ;-) It's an open source project after all so this is my contribution.
0
 

Author Comment

by:danz67
ID: 37767963
Everything you say is right, and accept all of your suggestions that I have been very useful. I posted this last question just because you asked me before.

Now it's up to me ... Hello and thank you all;)
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Suggested Solutions

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now