2014-06-06 06:19:45 +12:00
#region License Information ( GPL v3 )
/ *
ShareX - A program that allows you to take screenshots and share any file type
Copyright ( C ) 2007 - 2014 ShareX Developers
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
Optionally you can also view the license at < http : //www.gnu.org/licenses/>.
* /
#endregion License Information ( GPL v3 )
2014-06-06 09:58:04 +12:00
// Credits: https://github.com/KamilKZ
2014-06-06 06:19:45 +12:00
using HelpersLib ;
using Newtonsoft.Json ;
using System ;
using System.Collections.Generic ;
using System.Collections.Specialized ;
using System.IO ;
using System.Text.RegularExpressions ;
using UploadersLib.HelperClasses ;
namespace UploadersLib.FileUploaders
{
public sealed class Copy : FileUploader , IOAuth
{
public OAuthInfo AuthInfo { get ; set ; }
public CopyAccountInfo AccountInfo { get ; set ; }
public string UploadPath { get ; set ; }
2014-06-10 05:26:23 +12:00
public CopyURLType URLType { get ; set ; }
2014-06-06 06:19:45 +12:00
private const string APIVersion = "1" ;
private const string URLAPI = "https://api.copy.com/rest" ;
private const string URLAccountInfo = URLAPI + "/user" ;
private const string URLFiles = URLAPI + "/files" ;
private const string URLMetaData = URLAPI + "/meta/" ;
private const string URLLinks = URLAPI + "/links" ;
private const string URLPublicDirect = "http://copy.com" ;
private const string URLRequestToken = "https://api.copy.com/oauth/request" ;
private const string URLAuthorize = "https://www.copy.com/applications/authorize" ;
private const string URLAccessToken = "https://api.copy.com/oauth/access" ;
2014-06-06 07:47:36 +12:00
private readonly NameValueCollection APIHeaders = new NameValueCollection { { "X-Api-Version" , APIVersion } } ;
2014-06-06 06:19:45 +12:00
public Copy ( OAuthInfo oauth )
{
AuthInfo = oauth ;
}
public Copy ( OAuthInfo oauth , CopyAccountInfo accountInfo )
: this ( oauth )
{
AccountInfo = accountInfo ;
}
// https://developers.copy.com/documentation#authentication/oauth-handshake
// https://developers.copy.com/console
public string GetAuthorizationURL ( )
{
2014-06-06 07:47:36 +12:00
return GetAuthorizationURL ( URLRequestToken , URLAuthorize , AuthInfo , new Dictionary < string , string > { { "oauth_callback" , Links . URL_CALLBACK } } ) ;
2014-06-06 06:19:45 +12:00
}
public bool GetAccessToken ( string verificationCode = null )
{
AuthInfo . AuthVerifier = verificationCode ;
return GetAccessToken ( URLAccessToken , AuthInfo ) ;
}
#region Copy accounts
// https://developers.copy.com/documentation#api-calls/profile
public CopyAccountInfo GetAccountInfo ( )
{
CopyAccountInfo account = null ;
if ( OAuthInfo . CheckOAuth ( AuthInfo ) )
{
string query = OAuthManager . GenerateQuery ( URLAccountInfo , null , HttpMethod . GET , AuthInfo ) ;
string response = SendRequest ( HttpMethod . GET , query , null , ResponseType . Text , APIHeaders ) ;
if ( ! string . IsNullOrEmpty ( response ) )
{
account = JsonConvert . DeserializeObject < CopyAccountInfo > ( response ) ;
if ( account ! = null )
{
AccountInfo = account ;
}
}
}
return account ;
}
#endregion Copy accounts
#region Files and metadata
2014-06-10 05:26:23 +12:00
// https://developers.copy.com/documentation#api-calls/filesystem - Download Raw File Conents
// GET https://api.copy.com/rest/files/PATH/TO/FILE
2014-06-06 06:19:45 +12:00
public bool DownloadFile ( string path , Stream downloadStream )
{
if ( ! string . IsNullOrEmpty ( path ) & & OAuthInfo . CheckOAuth ( AuthInfo ) )
{
string url = Helpers . CombineURL ( URLFiles , Helpers . URLPathEncode ( path ) ) ;
string query = OAuthManager . GenerateQuery ( url , null , HttpMethod . GET , AuthInfo ) ;
return SendRequest ( HttpMethod . GET , downloadStream , query ) ;
}
return false ;
2014-06-10 05:26:23 +12:00
}
2014-06-06 06:19:45 +12:00
// https://developers.copy.com/documentation#api-calls/filesystem - Create File or Directory
2014-06-06 07:47:36 +12:00
// POST https://api.copy.com/rest/files/PATH/TO/FILE?overwrite=true
2014-06-06 06:19:45 +12:00
public UploadResult UploadFile ( Stream stream , string path , string fileName )
{
if ( ! OAuthInfo . CheckOAuth ( AuthInfo ) )
{
Errors . Add ( "Copy login is required." ) ;
return null ;
}
string url = Helpers . CombineURL ( URLFiles , Helpers . URLPathEncode ( path ) ) ;
Dictionary < string , string > args = new Dictionary < string , string > ( ) ;
args . Add ( "overwrite" , "true" ) ;
string query = OAuthManager . GenerateQuery ( url , args , HttpMethod . POST , AuthInfo ) ;
// There's a 1GB and 5 hour(max time for a single upload) limit to all uploads through the API.
UploadResult result = UploadData ( stream , query , fileName , "file" , null , null , APIHeaders ) ;
if ( result . IsSuccess )
{
CopyUploadInfo content = JsonConvert . DeserializeObject < CopyUploadInfo > ( result . Response ) ;
if ( content ! = null )
{
2014-06-06 07:47:36 +12:00
AllowReportProgress = false ;
2014-06-10 05:26:23 +12:00
result . URL = CreatePublicURL ( content . objects [ 0 ] . path , URLType ) ;
2014-06-06 06:19:45 +12:00
}
}
return result ;
}
2014-06-10 05:26:23 +12:00
// https://developers.copy.com/documentation#api-calls/filesystem - Read Root Directory
// GET https://api.copy.com/rest/meta/copy
2014-06-06 06:19:45 +12:00
public CopyContentInfo GetMetadata ( string path )
{
CopyContentInfo contentInfo = null ;
if ( OAuthInfo . CheckOAuth ( AuthInfo ) )
{
string url = Helpers . CombineURL ( URLMetaData , Helpers . URLPathEncode ( path ) ) ;
string query = OAuthManager . GenerateQuery ( url , null , HttpMethod . GET , AuthInfo ) ;
string response = SendRequest ( HttpMethod . GET , query ) ;
if ( ! string . IsNullOrEmpty ( response ) )
{
contentInfo = JsonConvert . DeserializeObject < CopyContentInfo > ( response ) ;
}
}
return contentInfo ;
}
2014-06-10 05:26:23 +12:00
#endregion Files and metadata
public override UploadResult Upload ( Stream stream , string fileName )
2014-06-06 06:19:45 +12:00
{
2014-06-10 05:26:23 +12:00
return UploadFile ( stream , UploadPath , fileName ) ;
2014-06-06 06:19:45 +12:00
}
2014-06-10 05:26:23 +12:00
public string GetLinkURL ( CopyLinksInfo link , string path , CopyURLType urlType = CopyURLType . Default )
2014-06-06 06:19:45 +12:00
{
2014-06-10 05:26:23 +12:00
switch ( urlType )
2014-06-06 06:19:45 +12:00
{
2014-06-10 05:26:23 +12:00
case CopyURLType . Default :
return link . url ;
2014-06-06 06:19:45 +12:00
2014-06-10 05:26:23 +12:00
case CopyURLType . Shortened :
return link . url_short ;
2014-06-06 06:19:45 +12:00
2014-06-10 05:26:23 +12:00
case CopyURLType . Direct :
//use regex to get everything past the last '/' in path - the filename
//default = 'http://www.copy.com/s/' + link.id + file(regex)
//direct = 'http://copy.com/' + link.id + file(regex)
//NOTE: link.id and filename need a '/' between, but the regex should provide that...
//NOTE2: 'default' will use link.url because that's what the official docs state, even though it's not the true link,
//links can be a gallery, and the link.url is the link to the gallery..
2014-06-06 06:19:45 +12:00
2014-06-10 05:26:23 +12:00
Match match = Regex . Match ( path , @"\/[^\/]+$" ) ; // one '/' and multiple 'not /' and end
if ( match . Success )
2014-06-06 06:19:45 +12:00
{
2014-06-10 05:26:23 +12:00
return URLPublicDirect + "/" + link . id + match . ToString ( ) ;
2014-06-06 06:19:45 +12:00
}
else
{
2014-06-10 05:26:23 +12:00
return string . Empty ;
2014-06-06 06:19:45 +12:00
}
}
2014-06-10 05:26:23 +12:00
return string . Empty ; //theoretically this shouldn't happen as urlType will default to 'Default' and all options are covered, but an error is an error...
2014-06-06 06:19:45 +12:00
}
2014-06-10 05:26:23 +12:00
public string CreatePublicURL ( string path , CopyURLType urlType = CopyURLType . Default )
2014-06-06 06:19:45 +12:00
{
path = path . Trim ( '/' ) ;
string url = Helpers . CombineURL ( URLLinks , Helpers . URLPathEncode ( path ) ) ;
string query = OAuthManager . GenerateQuery ( url , null , HttpMethod . POST , AuthInfo ) ;
CopyLinkRequest publicLink = new CopyLinkRequest ( ) ;
publicLink . @public = true ;
publicLink . name = "ShareX" ;
publicLink . paths = new string [ ] { path } ;
2014-06-06 07:47:36 +12:00
2014-06-06 06:19:45 +12:00
string content = JsonConvert . SerializeObject ( publicLink ) ;
string response = SendRequest ( HttpMethod . POST , query , content , null , ResponseType . Text , APIHeaders ) ;
if ( ! string . IsNullOrEmpty ( response ) )
{
2014-06-10 05:26:23 +12:00
CopyLinksInfo link = JsonConvert . DeserializeObject < CopyLinksInfo > ( response ) ;
return GetLinkURL ( link , path , urlType ) ;
2014-06-06 06:19:45 +12:00
}
2014-06-06 07:47:36 +12:00
return string . Empty ;
2014-06-06 06:19:45 +12:00
}
2014-06-10 05:26:23 +12:00
public string GetPublicURL ( string path , CopyURLType urlType = CopyURLType . Default )
2014-06-06 06:19:45 +12:00
{
path = path . Trim ( '/' ) ;
CopyContentInfo fileInfo = GetMetadata ( path ) ;
2014-06-06 07:47:36 +12:00
foreach ( CopyLinksInfo link in fileInfo . links )
2014-06-06 06:19:45 +12:00
{
2014-06-10 05:26:23 +12:00
if ( ! link . expired & & link . @public )
2014-06-06 06:19:45 +12:00
{
2014-06-10 05:26:23 +12:00
return GetLinkURL ( link , path , urlType ) ;
2014-06-06 06:19:45 +12:00
}
}
2014-06-06 07:47:36 +12:00
return string . Empty ;
2014-06-06 06:19:45 +12:00
}
public static string TidyUploadPath ( string uploadPath )
{
if ( ! string . IsNullOrEmpty ( uploadPath ) )
{
return uploadPath . Trim ( ) . Replace ( '\\' , '/' ) . Trim ( '/' ) + "/" ;
}
return string . Empty ;
}
}
2014-06-10 05:26:23 +12:00
public enum CopyURLType
{
Default ,
Shortened ,
Direct
}
2014-06-06 06:19:45 +12:00
public class CopyAccountInfo
{
public long id { get ; set ; } // The user's unique Copy ID.
public string first_name { get ; set ; } // The user's first name.
public string last_name { get ; set ; } // The user's last name.
public CopyStorageInfo storage { get ; set ; }
public string email { get ; set ; }
}
public class CopyStorageInfo
{
public long used { get ; set ; } // The user's used quota outside of shared folders (bytes).
public long quota { get ; set ; } // The user's total quota allocation (bytes).
public long saved { get ; set ; } // The user's saved quota through shared folders (bytes).
}
public class CopyLinkRequest
{
public bool @public { get ; set ; }
public string name { get ; set ; }
public string [ ] paths { get ; set ; }
}
public class CopyLinksInfo
{
public string id { get ; set ; }
public string name { get ; set ; }
public bool @public { get ; set ; }
public bool expires { get ; set ; }
public bool expired { get ; set ; }
public string url { get ; set ; }
public string url_short { get ; set ; }
public string creator_id { get ; set ; }
public string company_id { get ; set ; }
public bool confirmation_required { get ; set ; }
public string status { get ; set ; }
public string permissions { get ; set ; }
}
2014-06-10 05:26:23 +12:00
public class CopyContentInfo // https://api.copy.com/rest/meta also works on 'rest/files'
2014-06-06 06:19:45 +12:00
{
public string id { get ; set ; } // Internal copy name
public string path { get ; set ; } // hmm?
public string name { get ; set ; } // Human readable (Filesystem) folder name
public string type { get ; set ; } // "inbox", "root", "copy", "dir", "file"?
public bool stub { get ; set ; } // 'The stub attribute you see on all of the nodes represents if the specified node is incomplete, that is, if the children have not all been delivered to you. Basically, they will always be a stub, unless you are looking at that item directly.'
public long size { get ; set ; } // size of the folder/file
public long date_last_synced { get ; set ; }
public bool @public { get ; set ; } // is available to public; isnt everything private but shared in copy???
public string url { get ; set ; } // web access url (private)
public long revision_id { get ; set ; } // Revision of content
public CopyLinksInfo [ ] links { get ; set ; } // links array
public CopyContentInfo [ ] children { get ; set ; } // Children
}
public class CopyUploadInfo
{
public CopyContentInfo [ ] objects { get ; set ; }
}
}