{***************************************************************************}
{ TMS DropBox Cloud access                                                  }
{ for Delphi & C++Builder                                                   }
{                                                                           }
{ written by TMS Software                                                   }
{            copyright © 2012                                               }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{                                                                           }
{ The source code is given as is. The author is not responsible             }
{ for any possible damage done due to the use of this code.                 }
{ The component can be freely used in any application. The complete         }
{ source code remains property of the author and may not be distributed,    }
{ published, given or sold in any form as such. No parts of the source      }
{ code can be included in any other component or application without        }
{ written authorization of the author.                                      }
{***************************************************************************}

unit CloudDropBox;

interface

uses
  Classes, CloudBase, SHDocVw, Windows, ComCtrls;

const
  CALLBACK_URL = 'http://www.tmssoftware.com/';

  MAJ_VER = 1; // Major version nr.
  MIN_VER = 0; // Minor version nr.
  REL_VER = 0; // Release nr.
  BLD_VER = 0; // Build nr.

type
  TDropBoxItems = class;

  TDropBoxItem = class(TCloudItem)
  private
    FLink: string;
    FAccessValue: string;
    FRevision: integer;
  protected
    property AccessValue: string read FAccessValue write FAccessValue;
  public
    procedure Assign(Source: TPersistent); override;
    function FullPath: string;
    property Link: string read FLink write FLink;
    property Revision: integer read FRevision write FRevision;
  end;

  TDropBoxItems = class(TCloudItems)
  private
    FFolder: TDropBoxItem;
    function GetItems(index: integer): TDropBoxItem;
    procedure SetItems(index: integer; const Value: TDropBoxItem);
  protected
    function GetItemClass: TCollectionItemClass; override;
  public
    constructor Create(AFolder: TDropBoxItem);
    function Add: TDropBoxItem;
    function Insert(Index: integer): TDropBoxItem;
    property Items[index: integer]: TDropBoxItem read GetItems write SetItems;
    property Folder: TDropBoxItem read FFolder;
  end;

  TDropBoxInfo = class(TPersistent)
  private
    FQuota: double;
    FUID: string;
    FCountry: string;
    FQuotaNormal: double;
    FQuotaShared: double;
    FUserName: string;
  public
  published
    property UserName: string read FUserName write FUserName;
    property Country: string read FCountry write FCountry;
    property UID: string read FUID write FUID;
    property Quota: double read FQuota write FQuota;
    property QuotaShared: double read FQuotaShared write FQuotaShared;
    property QuotaNormal: double read FQuotaNormal write FQuotaNormal;
  end;

  TAdvDropBox = class(TCloudStorage)
  private
    FCallBackURL: string;
    FInfo: TDropBoxInfo;
    procedure SetInfo(const Value: TDropBoxInfo);
    function GetDDrive: TDropBoxItems;
  protected
    function GetVersionNr: integer; override;
    function GetAuthURL: string;
    function CreateCloudItems: TCloudItems; override;
    procedure GetAuthToken;
    {$IF CompilerVersion >= 23.0}
    procedure DoCallBack(ASender: TObject;
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
      Headers: OleVariant; var Cancel: WordBool);
    {$ELSE}
    procedure DoCallBack(ASender: TObject;
      const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
      Headers: OleVariant; var Cancel: WordBool);
    {$IFEND}
    function UploadInt(Folder: TDropBoxItem; const FolderName, FileName:string): TDropBoxItem;
    function DownloadInt(const FileName: string; const TargetFile: string; Size: int64): boolean;
  public
    function DropBoxDate(s: string): TDateTime;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function TestTokens: boolean; override;
    procedure DoAuth; override;
    procedure GetAccountInfo;
    procedure GetDriveInfo; override;
    function GetFileInfo(const FileName:string): TDropBoxItem;
    procedure GetFolderInfo(const FolderName: string; Items: TDropBoxItems);
    function Search(const FolderName, Query:string): TDropBoxItems;
    function Download(ci: TCloudItem; const TargetFile: string): boolean; override;
    function Download(const FileName: string; const TargetFile: string): boolean; override;
    function Upload(Folder: TDropBoxItem; const FileName:string): TDropBoxItem; overload;
    function Upload(FolderName: string; const FileName:string): TDropBoxItem; overload;
    function Delete(ci: TCloudItem): boolean; override;
    function Delete(const FileName: string): boolean; override;
    function CreateFolder(ci: TCloudItem; const FolderName: string): TCloudItem; override;
    function GetLink(Item: TDropBoxItem): string;
    property Info: TDropBoxInfo read FInfo write SetInfo;
    property DDrive: TDropBoxItems read GetDDrive;
    property CallbackURL: string read FCallBackURL write FCallBackURL;
  published
  end;

implementation

uses
  DBXJSON, SysUtils;

{ TAdvDropBox }

constructor TAdvDropBox.Create(AOwner: TComponent);
begin
  inherited;
  FInfo := TDropBoxInfo.Create;
  FCallBackURL := CALLBACK_URL;
end;

function TAdvDropBox.CreateCloudItems: TCloudItems;
begin
  Result := TDropBoxItems.Create(nil);
end;

function TAdvDropBox.CreateFolder(ci: TCloudItem;
  const FolderName: string): TCloudItem;
var
  url: string;
  sig,sigbase: string;
  fname,delfname: string;
  o: TJSONObject;
  res: boolean;
  Folder: TDropBoxItem;
begin
  Result := nil;
  res := false;

  if Assigned(ci) then
  begin
    Folder := ci as TDropBoxItem;
    if not (Folder.ItemType = ciFolder) then
      Exit;
  end
  else
    Folder := nil;

  if Assigned(Folder) and (Folder.FullPath <> '') then
    fname := '/' + Folder.FullPath + '/' + FolderName
  else
    fname := '/' + FolderName;

  url := 'https://api.dropbox.com/1/fileops/create_folder';

  AddOAuthRequestParams(App.Key, Token_Access);
  RequestParams.Values['path'] := URLEncode(fname);
  RequestParams.Values['root'] := 'dropbox';

  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  o := GetJSONObject(url);

  if Assigned(o) then
  begin
    delfname := GetJSONProp(o,'path');
    res := delfname = fname;
    o.Free;
  end;

  if res then
  begin
    if Assigned(Folder) then
      Result := Folder.Folder.Add
    else
      Result := DDrive.Add;

    Result.FileName := FolderName;
    Result.ItemType := ciFolder;
  end;
end;

function TAdvDropBox.Delete(ci: TCloudItem): boolean;
var
  Item: TDropBoxItem;
begin
  Result := false;

  inherited;

  if not (ci is TDropBoxItem) then
    Exit;

  Item := (ci as TDropBoxItem);

  if Assigned(Item) then
    Result := Delete('/' + Item.FullPath);
end;

function TAdvDropBox.Delete(const FileName: string): boolean;
var
  url: string;
  sig,sigbase: string;
  fname,delfname: string;
  o: TJSONObject;

begin
  Result := false;

  fname := '/' + FileName;
  url := 'https://api.dropbox.com/1/fileops/delete';

  AddOAuthRequestParams(App.Key, Token_Access);
  RequestParams.Values['path'] := URLEncode(fname);
  RequestParams.Values['root'] := 'dropbox';

  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  o := GetJSONObject(url);

  if Assigned(o) then
  begin
    delfname := GetJSONProp(o,'path');
    Result := Pos(FileName,delfname) > 0;
    o.Free;
  end;
end;

destructor TAdvDropBox.Destroy;
begin
  FInfo.Free;
  inherited;
end;

procedure TAdvDropBox.DoAuth;
var
  resdat: string;
  url: string;

begin
  resdat := string(HttpsGet(GetAuthURL));

  Log('AUTH GET: ' + string(resdat));

  if pos('oauth_token_secret', resdat) > 0 then
  begin
    System.Delete(resdat,1,length('oauth_token_secret='));
    Token_Auth_Secret := copy(resdat, 1, pos('&', resdat) - 1);
    System.Delete(resdat,1,pos('&',resdat));
    System.Delete(resdat,1,length('oauth_token='));
    Token_Auth := resdat;
  end;

  url := 'https://www.dropbox.com/1/oauth/authorize?oauth_token='+ Token_Auth+'&oauth_callback='+URLEncode(FCallBackURL);

  if Assigned(AuthBrowser) then
  begin
    AuthBrowser.OnBeforeNavigate2 := DoCallBack;
    AuthBrowser.Navigate(url);
  end
  else
  begin
    CreateOAuthForm(url, DoCallBack, nil);
  end;
end;

{$IF CompilerVersion >= 23.0}
procedure TAdvDropBox.DoCallBack(ASender: TObject; const pDisp: IDispatch;
  const URL, Flags, TargetFrameName, PostData, Headers: OleVariant;
  var Cancel: WordBool);
{$ELSE}
procedure TAdvDropBox.DoCallBack(ASender: TObject; const pDisp: IDispatch;
  var URL, Flags, TargetFrameName, PostData, Headers: OleVariant;
  var Cancel: WordBool);
{$IFEND}
var
  s: string;
begin
  s := URL;

  if pos(FCallBackURL,s) > 0 then
  begin
    CloseOAuthForm;
    GetAuthToken;
  end;
end;

function TAdvDropBox.DownloadInt(const FileName: string; const TargetFile: string; Size: int64): boolean;
var
  url: string;
  sig,sigbase: string;
  fname: string;
  resdat: ansistring;
begin
  fname := '/' + FileName;
  url := 'https://api-content.dropbox.com/1/files';

  AddOAuthRequestParams(App.Key, Token_Access);
  RequestParams.Values['path'] := URLEncode(fname);
  RequestParams.Values['root'] := 'dropbox';

  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  resdat := HttpsGet(url,TargetFile,Size);

  Result := true;
end;


function TAdvDropBox.Download(ci: TCloudItem; const TargetFile: string): boolean;
var
  Item: TDropBoxItem;
begin
  Result := false;

  inherited;

  if not (ci is TDropBoxItem) then
    Exit;

  Item := (ci as TDropBoxItem);

  if Assigned(Item) and (Item is TDropBoxItem) then
  begin
    Item  := ci as TDropBoxItem;
    Result := DownloadInt(Item.FullPath,TargetFile, Item.Size);
  end;
end;

function TAdvDropBox.Download(const FileName: string; const TargetFile: string): boolean;
begin
  Result := DownLoadInt(FileName,TargetFile,0);
end;

function TAdvDropBox.DropBoxDate(s: string): TDateTime;
var
  da,mo,ye,ho,mi,se:word;
  su: string;
  e: integer;
begin
  System.Delete(s,1,pos(',',s) + 1);

  su := Copy(s,1,Pos(' ',s) - 1);
  System.Delete(s,1,Pos(' ',s));

  val(su,da,e);

  su := Copy(s,1,Pos(' ',s) - 1);
  System.Delete(s,1,Pos(' ',s));

  mo := 1;

  if su = 'Jan' then
    mo := 1;
  if su = 'Feb' then
    mo := 2;
  if su = 'Mar' then
    mo := 3;
  if su = 'Apr' then
    mo := 4;
  if su = 'May' then
    mo := 5;
  if su = 'Jun' then
    mo := 6;
  if su = 'Jul' then
    mo := 7;
  if su = 'Aug' then
    mo := 8;
  if su = 'Sep' then
    mo := 9;
  if su = 'Oct' then
    mo := 10;
  if su = 'Nov' then
    mo := 11;
  if su = 'Dec' then
    mo := 12;

  su := Copy(s,1,Pos(' ',s) - 1);
  System.Delete(s,1,Pos(' ',s));

  val(su,ye,e);

  su := Copy(s,1,Pos(':',s) - 1);
  System.Delete(s,1,Pos(':',s));

  val(su,ho,e);

  su := Copy(s,1,Pos(':',s) - 1);
  System.Delete(s,1,Pos(':',s));

  val(su,mi,e);

  su := Copy(s,1,Pos(' ',s) - 1);

  val(su,se,e);

  Result := EncodeDate(ye,mo,da) + EncodeTime(ho,mi,se,0);
end;

procedure TAdvDropBox.GetAccountInfo;
var
  url: string;
  sig,sigbase: string;
  o,quotao: TJSONObject;
  obj: TJSONValue;
begin
  url := 'https://api.dropbox.com/1/account/info';

  AddOAuthRequestParams(App.Key, Token_Access);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  o := GetJSONObject(url);

  if Assigned(o) then
  begin
    try
      FInfo.UserName := GetJSONProp(o,'display_name');
      FInfo.Country := GetJSONProp(o,'country');
      FInfo.UID := GetJSONProp(o,'uid');
      obj := GetJSONValue(o,'quota_info');
      if Assigned(obj) then
      begin
        quotao := obj as TJSONOBject;
        FInfo.Quota := (GetJSONValue(quotao,'quota') as TJSONNumber).AsDouble;
        FInfo.QuotaShared := (GetJSONValue(quotao,'shared') as TJSONNumber).AsDouble;
        FInfo.QuotaNormal := (GetJSONValue(quotao,'normal') as TJSONNumber).AsDouble;
      end;
    finally
      o.Free;
    end;
  end;
end;

function TAdvDropBox.GetFileInfo(const Filename: string): TDropBoxItem;
var
  url,sz: string;
  sig,sigbase: string;
  dt: string;
  fo: TJSONObject;
begin
  Result := nil;

  url := 'https://api.dropbox.com/1/metadata/dropbox/' + SpaceEncode(FileName);

  AddOAuthRequestParams(App.Key, Token_Access);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  fo := GetJSONOBject(url);

  if Assigned(fo) then
  begin
    Result := TDropBoxItem.Create(nil);

    sz := GetJSONProp(fo,'bytes');
    Result.Size := StrToInt64T(sz);

    Result.Revision := (GetJSonValue(fo,'revision') as TJSONNumber).AsInt;

    Result.FileName := GetJSONProp(fo,'path');

    dt := GetJSONProp(fo,'client_mtime');
    if dt <> '' then
      Result.CreationDate := DropBoxDate(dt);

    dt := GetJSONProp(fo,'modified');
    if dt <> '' then
      Result.ModifiedDate := DropBoxDate(dt);

    if GetJSONValue(fo,'is_dir') is TJSONTrue then
      Result.ItemType := ciFolder
    else
      Result.ItemType := ciFile;

    fo.Free;
  end;
end;

procedure TAdvDropBox.GetFolderInfo(const FolderName: string; Items: TDropBoxItems);
var
  url: string;
  sig,sigbase,s,fpath: string;
  o,fo: TJSONObject;
  ja: TJSONArray;
  i,vp: integer;
  di: TDropBoxItem;
  dt,sz: string;
  subitems: TDropBoxItems;
begin
  url := 'https://api.dropbox.com/1/metadata/dropbox/' + SpaceEncode(FolderName);

  AddOAuthRequestParams(App.Key, Token_Access);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  o := GetJSONOBject(url);

  ja := GetJSONValue(o,'contents') as TJSONArray;

  try
    if Assigned(ja) then
    begin
      for i := 0 to ja.Size - 1 do
      begin
        fo := ja.Get(i) as TJSONObject;

        di := Items.Add;

        sz := GetJSONProp(fo, 'bytes');
        di.Size := StrToInt64T(sz);

        di.Revision := (GetJSonValue(fo,'revision') as TJSONNumber).AsInt;

        fpath := GetJSONProp(fo,'path');

        dt := GetJSONProp(fo,'client_mtime');
        if dt <> '' then
          di.CreationDate := DropBoxDate(dt);

        dt := GetJSONProp(fo,'modified');
        if dt <> '' then
          di.ModifiedDate := DropBoxDate(dt);

        if Pos('/',fpath) = 1 then
          System.Delete(fpath,1,1);

        s := fpath;

        while VarPos('/',s,vp) > 0 do
        begin
          System.Delete(s,1,vp);
        end;

        di.FileName := s;

        if GetJSONValue(fo,'is_dir') is TJSONTrue then
          di.ItemType := ciFolder
        else
          di.ItemType := ciFile;

        if di.ItemType = ciFolder then
        begin
          subitems := TDropBoxItems.Create(di);
          di.Folder := subitems;
          GetFolderInfo(fpath, di.Folder as TDropBoxItems);
        end;
      end;
    end;
  finally
    if Assigned(o) then
      o.Free;
  end;
end;

function TAdvDropBox.GetLink(Item: TDropBoxItem): string;
var
  url: string;
  sig,sigbase: string;
  o: TJSONObject;
begin
  Result := '';

  url := 'https://api.dropbox.com/1/media/dropbox/'+SpaceEncode(Item.FullPath);

  AddOAuthRequestParams(App.Key, Token_Access);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  o := GetJSONOBject(url);

  if Assigned(o) then
  begin
    try
      Result := GetJSONProp(o,'url');
    finally
      o.Free;
    end;
  end;
end;

function TAdvDropBox.GetVersionNr: integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

function TAdvDropBox.GetAuthURL: string;
var
  url: string;
  sig,sigbase: string;

begin
  url := 'https://api.dropbox.com/1/oauth/request_token';

  AddOAuthRequestParams(App.Key, Token_Access);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  Result := SignURL(url);

  Log('AUTH URL: '+ Result);
end;

function TAdvDropBox.GetDDrive: TDropBoxItems;
begin
  Result := Drive as TDropBoxItems;
end;

procedure TAdvDropBox.GetDriveInfo;
begin
  DDrive.Clear;
  GetFolderInfo('',DDrive);
end;

procedure TAdvDropBox.GetAuthToken;
var
  url: string;
  sig,sigbase: string;
  resdat: string;
begin
  url := 'https://api.dropbox.com/1/oauth/access_token';

  AddOAuthRequestParams(App.Key, Token_Auth);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Auth_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  resdat := string(HttpsGet(url));

  if pos('oauth_token_secret', resdat) > 0 then
  begin
    System.Delete(resdat,1,length('oauth_token_secret='));
    Token_Access_Secret := copy(resdat, 1, pos('&', resdat) - 1);
    System.Delete(resdat,1,pos('&',resdat));
    System.Delete(resdat,1,length('oauth_token='));
    Token_access := copy(resdat,1, pos('&',resdat) - 1);
    Log('GOT ACCESS TOKENS: ' + Token_Access + ' / ' + Token_Access_Secret);
    DoReceivedAccessToken;
  end;
end;

function TAdvDropBox.Search(const FolderName,Query: string): TDropBoxItems;
var
  url: string;
  sig,sigbase,fpath: string;
  o,fo: TJSONObject;
  jv: TJSONValue;
  ja: TJSONArray;
  i: integer;
  di: TDropBoxItem;
  dt: string;
  resdat,sz : string;
  sl: TStringList;
begin
  Result := nil;

  url := 'https://api.dropbox.com/1/search/dropbox/' + SpaceEncode(FolderName);

  AddOAuthRequestParams(App.Key, Token_Access);
  RequestParams.Values['query'] := URLEncode(Query);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');

  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  resdat := string(HttpsGet(url));

  sl := TStringList.Create;
  try
    sl.Text := resdat;
    dt := '{ "contents": ' + sl.Strings[0] + '}';
  finally
    sl.Free;
  end;

  jv := TJSONOBject.ParseJSONValue(dt);

  if Assigned(jv) and (jv is TJSONObject) then
  begin
    try
      o := jv as TJSONObject;
      ja := GetJSONValue(o,'contents') as TJSONArray;

      if Assigned(ja) then
      begin
        Result := TDropBoxItems.Create(nil);

        for i := 0 to ja.Size - 1 do
        begin
          fo := ja.Get(i) as TJSONObject;

          di := Result.Add;

          sz := GetJSONProp(fo, 'bytes');
          di.Size := StrToInt64T(sz);

          di.Revision := (GetJSonValue(fo,'revision') as TJSONNumber).AsInt;

          fpath := GetJSONProp(fo,'path');
          di.FileName := fpath;

          dt := GetJSONProp(fo,'client_mtime');
          if dt <> '' then
            di.CreationDate := DropBoxDate(dt);

          dt := GetJSONProp(fo,'modified');
          if dt <> '' then
            di.ModifiedDate := DropBoxDate(dt);

          if GetJSONValue(fo,'is_dir') is TJSONTrue then
            di.ItemType := ciFolder
          else
            di.ItemType := ciFile;
        end;
      end;
    finally
      jv.Free;
    end;
  end;
end;

procedure TAdvDropBox.SetInfo(const Value: TDropBoxInfo);
begin
  FInfo.Assign(Value);
end;

function TAdvDropBox.TestTokens: boolean;
var
  url: string;
  sigbase,sig:string;
  o: TJSONObject;
begin
  Result := false;

  if Token_Access = '' then
    Exit;

  url := 'https://api.dropbox.com/1/account/info';

  AddOAuthRequestParams(App.Key, Token_Access);
  sigbase := GetOAuthSigBase(url,App.Key,'GET');
  sig := GetSignature(sigbase, App.Secret, Token_Access_Secret);
  RequestParams.Values['oauth_signature'] := URLEncodeRFC3986(sig);
  url := SignURL(url);

  o := GetJSONObject(url);

  if Assigned(o) then
  begin
    Result := GetJSONProp(o,'uid') <> '';
    o.Free;
  end;
end;

function TAdvDropBox.UploadInt(Folder: TDropBoxItem; const FolderName, FileName:string): TDropBoxItem;
var
  url,fpath: string;
  nonce: string;
  timestamp: string;
  s,sz: string;
  resdat: string;
  efn,efnc: string;
  jv: TJSONValue;
  fo: TJSONObject;
  vp: Integer;
begin
  Result := nil;

  efn := ExtractFileName(FileName);

  if FolderName <> '' then
    efnc := FolderName + '/' + efn
  else
    efnc := efn;

  efnc := URLEncode(efnc);

  url := 'https://api-content.dropbox.com/1/files_put';

  nonce := GenerateNonce;
  timestamp := GenerateTimeStamp;

  // PLAINTEXT signature
  url := url
    + '?root=dropbox'
    + '&path=' + efnc
    + '&oauth_consumer_key='+ App.Key
    + '&oauth_token='+ Token_Access
    + '&oauth_nonce='+ nonce
    + '&oauth_signature_method=PLAINTEXT'
    + '&oauth_timestamp='+ timestamp
    + '&oauth_version=1.0'
    + '&oauth_signature=' + URLEncode(App.Secret+ '&' + Token_Access_Secret);

  resdat := string(HttpsPut(url,ExtractFilePath(FileName),efn,nil,'',hcPut));

  // parse JSON
  if resdat <> '' then
  begin
    jv := TJSONOBject.ParseJSONValue(resdat);

    if Assigned(jv) then
    begin
      try
        fo := jv as TJSONObject;
        fpath := GetJSONProp(fo,'path');

        if (fpath <> '') then
        begin

          if Assigned(Folder) then
            Result := Folder.Folder.Add as TDropBoxItem
          else
            Result := DDrive.Add;

          if Pos('/',fpath) = 1 then
            System.Delete(fpath,1,1);
           s := fpath;

          while VarPos('/',s,vp) > 0 do
            System.Delete(s,1,vp);

          Result.FileName := s;

          sz := GetJSONProp(fo, 'bytes');
          Result.Size := StrToInt64T(sz);

          if GetJSONValue(fo,'is_dir') is TJSONTrue then
            Result.ItemType := ciFolder
          else
            Result.ItemType := ciFile;
        end;
      finally
        jv.Free;
      end;
    end;
  end;
end;

function TAdvDropBox.Upload(Folder: TDropBoxItem; const FileName: string): TDropBoxItem;
var
  FolderName: string;

begin
  // use the folder name of the parent folder
  if Assigned(Folder) and (Folder.FullPath <> '') then
    FolderName := Folder.FullPath
  else
    FolderName := '';

  Result := UploadInt(Folder, FolderName, FileName);
end;

function TAdvDropBox.Upload(FolderName: string; const FileName:string): TDropBoxItem;
begin
  Result := UploadInt(nil,FolderName,FileName);
end;

{------------------------------------------------------------------------------}

{ TDropBoxItems }

function TDropBoxItems.Add: TDropBoxItem;
begin
  Result := TDropBoxItem(inherited Add);
end;

constructor TDropBoxItems.Create(AFolder: TDropBoxItem);
begin
  inherited Create;
  FFolder := AFolder;
end;

function TDropBoxItems.GetItemClass: TCollectionItemClass;
begin
  Result := TDropBoxItem;
end;

function TDropBoxItems.GetItems(index: integer): TDropBoxItem;
begin
  Result := TDropBoxItem(inherited Items[Index]);
end;

function TDropBoxItems.Insert(Index: integer): TDropBoxItem;
begin
  Result := TDropBoxItem(inherited Insert(Index));
end;

procedure TDropBoxItems.SetItems(index: integer; const Value: TDropBoxItem);
begin
  inherited Items[Index] := Value;
end;

{------------------------------------------------------------------------------}

{ TDropBoxItem }

procedure TDropBoxItem.Assign(Source: TPersistent);
begin
  inherited;
  if (Source is TDropBoxItem) then
  begin
    FRevision := (Source as TDropBoxItem).Revision;
  end;
end;


function TDropBoxItem.FullPath: string;
var
  d: TDropBoxItems;
begin
  Result := '';
  d := nil;

  if Assigned(Collection) then
    d := Collection as TDropBoxItems;

  while Assigned(d) and Assigned(d.Folder) do
  begin
    Result := d.Folder.FileName + '/' + Result;
    d := d.Folder.Collection as TDropBoxItems;
  end;

  Result := Result + FileName;
end;

end.
