2014-12-26 07:36:15 +13:00
#region License Information ( GPL v3 )
/ *
ShareX - A program that allows you to take screenshots and share any file type
2019-01-02 20:43:52 +13:00
Copyright ( c ) 2007 - 2019 ShareX Team
2014-12-26 07:36:15 +13:00
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 )
using Newtonsoft.Json ;
using ShareX.HelpersLib ;
2014-12-28 23:07:34 +13:00
using ShareX.UploadersLib.Properties ;
2014-12-26 07:36:15 +13:00
using System.Collections.Generic ;
2018-04-12 13:45:12 +12:00
using System.Collections.Specialized ;
2014-12-26 10:23:42 +13:00
using System.ComponentModel ;
2016-06-28 05:20:55 +12:00
using System.Drawing ;
2014-12-26 07:36:15 +13:00
using System.IO ;
2016-05-01 20:09:11 +12:00
using System.Windows.Forms ;
2014-12-26 07:36:15 +13:00
namespace ShareX.UploadersLib.FileUploaders
{
2016-03-23 06:24:41 +13:00
public class OneDriveFileUploaderService : FileUploaderService
{
public override FileDestination EnumValue { get ; } = FileDestination . OneDrive ;
2016-06-28 05:20:55 +12:00
public override Icon ServiceIcon = > Resources . OneDrive ;
2016-03-24 07:47:59 +13:00
public override bool CheckConfig ( UploadersConfig config )
2016-03-23 06:24:41 +13:00
{
2018-04-14 03:10:58 +12:00
return OAuth2Info . CheckOAuth ( config . OneDriveV2OAuth2Info ) ;
2016-03-23 06:24:41 +13:00
}
2016-04-04 21:53:11 +12:00
public override GenericUploader CreateUploader ( UploadersConfig config , TaskReferenceHelper taskInfo )
2016-03-23 06:24:41 +13:00
{
2018-04-14 03:10:58 +12:00
return new OneDrive ( config . OneDriveV2OAuth2Info )
2016-03-23 06:24:41 +13:00
{
2018-04-14 03:10:58 +12:00
FolderID = config . OneDriveV2SelectedFolder . id ,
2016-03-24 07:47:59 +13:00
AutoCreateShareableLink = config . OneDriveAutoCreateShareableLink
2016-03-23 06:24:41 +13:00
} ;
}
2016-05-01 20:09:11 +12:00
public override TabPage GetUploadersConfigTabPage ( UploadersConfigForm form ) = > form . tpOneDrive ;
2016-03-23 06:24:41 +13:00
}
2014-12-26 07:36:15 +13:00
public sealed class OneDrive : FileUploader , IOAuth2
{
2018-04-12 13:45:12 +12:00
private const string AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" ;
private const string TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token" ;
2018-04-17 06:15:46 +12:00
private const int MaxSegmentSize = 64 * 1024 * 1024 ; // 64 MiB
2018-04-12 13:45:12 +12:00
2014-12-26 07:36:15 +13:00
public OAuth2Info AuthInfo { get ; set ; }
public string FolderID { get ; set ; }
2014-12-26 10:23:42 +13:00
public bool AutoCreateShareableLink { get ; set ; }
2014-12-26 07:36:15 +13:00
2014-12-27 02:58:38 +13:00
public static OneDriveFileInfo RootFolder = new OneDriveFileInfo
{
2018-04-12 13:45:12 +12:00
id = "" , // empty defaults to root
2014-12-28 23:07:34 +13:00
name = Resources . OneDrive_RootFolder_Root_folder
2014-12-27 02:58:38 +13:00
} ;
2014-12-26 07:36:15 +13:00
public OneDrive ( OAuth2Info authInfo )
{
AuthInfo = authInfo ;
}
public string GetAuthorizationURL ( )
{
Dictionary < string , string > args = new Dictionary < string , string > ( ) ;
args . Add ( "client_id" , AuthInfo . Client_ID ) ;
2018-04-12 13:45:12 +12:00
args . Add ( "scope" , "offline_access files.readwrite" ) ;
2014-12-26 07:36:15 +13:00
args . Add ( "response_type" , "code" ) ;
args . Add ( "redirect_uri" , Links . URL_CALLBACK ) ;
2018-04-14 04:07:27 +12:00
if ( AuthInfo . Proof ! = null )
{
args . Add ( "code_challenge" , AuthInfo . Proof . CodeChallenge ) ;
args . Add ( "code_challenge_method" , AuthInfo . Proof . ChallengeMethod ) ;
}
2014-12-26 07:36:15 +13:00
2019-01-07 00:47:37 +13:00
return URLHelpers . CreateQueryString ( AuthorizationEndpoint , args ) ;
2014-12-26 07:36:15 +13:00
}
public bool GetAccessToken ( string code )
{
Dictionary < string , string > args = new Dictionary < string , string > ( ) ;
args . Add ( "client_id" , AuthInfo . Client_ID ) ;
args . Add ( "redirect_uri" , Links . URL_CALLBACK ) ;
args . Add ( "client_secret" , AuthInfo . Client_Secret ) ;
args . Add ( "code" , code ) ;
args . Add ( "grant_type" , "authorization_code" ) ;
2018-04-14 04:07:27 +12:00
if ( AuthInfo . Proof ! = null )
{
args . Add ( "code_verifier" , AuthInfo . Proof . CodeVerifier ) ;
}
2014-12-26 07:36:15 +13:00
2018-04-12 13:45:12 +12:00
string response = SendRequestURLEncoded ( HttpMethod . POST , TokenEndpoint , args ) ;
2014-12-26 07:36:15 +13:00
if ( ! string . IsNullOrEmpty ( response ) )
{
OAuth2Token token = JsonConvert . DeserializeObject < OAuth2Token > ( response ) ;
if ( token ! = null & & ! string . IsNullOrEmpty ( token . access_token ) )
{
token . UpdateExpireDate ( ) ;
AuthInfo . Token = token ;
return true ;
}
}
return false ;
}
public bool RefreshAccessToken ( )
{
if ( OAuth2Info . CheckOAuth ( AuthInfo ) & & ! string . IsNullOrEmpty ( AuthInfo . Token . refresh_token ) )
{
Dictionary < string , string > args = new Dictionary < string , string > ( ) ;
args . Add ( "client_id" , AuthInfo . Client_ID ) ;
args . Add ( "client_secret" , AuthInfo . Client_Secret ) ;
args . Add ( "refresh_token" , AuthInfo . Token . refresh_token ) ;
args . Add ( "grant_type" , "refresh_token" ) ;
2018-04-12 13:45:12 +12:00
string response = SendRequestURLEncoded ( HttpMethod . POST , TokenEndpoint , args ) ;
2014-12-26 07:36:15 +13:00
if ( ! string . IsNullOrEmpty ( response ) )
{
OAuth2Token token = JsonConvert . DeserializeObject < OAuth2Token > ( response ) ;
if ( token ! = null & & ! string . IsNullOrEmpty ( token . access_token ) )
{
token . UpdateExpireDate ( ) ;
string refresh_token = AuthInfo . Token . refresh_token ;
AuthInfo . Token = token ;
AuthInfo . Token . refresh_token = refresh_token ;
return true ;
}
}
}
return false ;
}
public bool CheckAuthorization ( )
{
if ( OAuth2Info . CheckOAuth ( AuthInfo ) )
{
if ( AuthInfo . Token . IsExpired & & ! RefreshAccessToken ( ) )
{
Errors . Add ( "Refresh access token failed." ) ;
return false ;
}
}
else
{
Errors . Add ( "Login is required." ) ;
return false ;
}
return true ;
}
2018-04-12 13:45:12 +12:00
private NameValueCollection GetAuthHeaders ( )
2014-12-26 07:36:15 +13:00
{
2018-04-12 13:45:12 +12:00
NameValueCollection headers = new NameValueCollection ( ) ;
headers . Add ( "Authorization" , "Bearer " + AuthInfo . Token . access_token ) ;
return headers ;
}
2014-12-26 09:01:22 +13:00
2018-04-12 13:45:12 +12:00
private string GetFolderUrl ( string folderID )
{
2014-12-26 19:17:38 +13:00
string folderPath ;
2018-04-12 13:45:12 +12:00
if ( ! string . IsNullOrEmpty ( folderID ) )
2014-12-26 19:17:38 +13:00
{
2018-04-12 13:45:12 +12:00
folderPath = URLHelpers . CombineURL ( "me/drive/items" , folderID ) ;
2014-12-26 19:17:38 +13:00
}
else
{
2018-04-12 13:45:12 +12:00
folderPath = "me/drive/root" ;
2014-12-26 19:17:38 +13:00
}
2018-04-12 13:45:12 +12:00
return folderPath ;
}
2018-04-14 17:32:48 +12:00
private string CreateSession ( string fileName )
2018-04-12 13:45:12 +12:00
{
2018-04-14 17:32:48 +12:00
string json = JsonConvert . SerializeObject ( new
{
item = new Dictionary < string , string >
{
{ "@microsoft.graph.conflictBehavior" , "replace" }
}
} ) ;
2014-12-26 09:01:22 +13:00
2018-04-12 13:45:12 +12:00
string folderPath = GetFolderUrl ( FolderID ) ;
2018-04-14 17:32:48 +12:00
string url = URLHelpers . BuildUri ( "https://graph.microsoft.com" , $"/v1.0/{folderPath}:/{fileName}:/createUploadSession" ) ;
2018-04-29 12:23:03 +12:00
AllowReportProgress = false ;
2018-10-18 05:06:06 +13:00
string response = SendRequest ( HttpMethod . POST , url , json , UploadHelpers . ContentTypeJSON , headers : GetAuthHeaders ( ) ) ;
2018-04-29 12:23:03 +12:00
AllowReportProgress = true ;
2018-04-14 17:32:48 +12:00
OneDriveUploadSession session = JsonConvert . DeserializeObject < OneDriveUploadSession > ( response ) ;
if ( session ! = null )
{
return session . uploadUrl ;
}
return null ;
}
2018-04-12 13:45:12 +12:00
2018-04-14 17:32:48 +12:00
public override UploadResult Upload ( Stream stream , string fileName )
{
if ( ! CheckAuthorization ( ) ) return null ;
2018-04-17 06:15:46 +12:00
string sessionUrl = CreateSession ( fileName ) ;
long position = 0 ;
UploadResult result = new UploadResult ( ) ;
do
2018-04-14 17:32:48 +12:00
{
2018-06-15 22:12:23 +12:00
result = SendRequestFileRange ( sessionUrl , stream , fileName , position , MaxSegmentSize ) ;
2018-04-14 17:32:48 +12:00
2018-04-17 06:15:46 +12:00
if ( result . IsSuccess )
{
position + = MaxSegmentSize ;
}
else
{
SendRequest ( HttpMethod . DELETE , sessionUrl ) ;
break ;
}
}
while ( position < stream . Length ) ;
2014-12-26 09:01:22 +13:00
if ( result . IsSuccess )
{
2014-12-26 19:17:38 +13:00
OneDriveFileInfo uploadInfo = JsonConvert . DeserializeObject < OneDriveFileInfo > ( result . Response ) ;
2014-12-26 10:23:42 +13:00
if ( AutoCreateShareableLink )
{
2018-04-17 06:15:46 +12:00
AllowReportProgress = false ;
2014-12-26 10:23:42 +13:00
result . URL = CreateShareableLink ( uploadInfo . id ) ;
}
else
{
2018-04-12 13:45:12 +12:00
result . URL = uploadInfo . webUrl ;
2014-12-26 10:23:42 +13:00
}
2014-12-26 09:01:22 +13:00
}
return result ;
}
2014-12-26 10:23:42 +13:00
public string CreateShareableLink ( string id , OneDriveLinkType linkType = OneDriveLinkType . Read )
{
string linkTypeValue ;
switch ( linkType )
{
case OneDriveLinkType . Embed :
linkTypeValue = "embed" ;
break ;
default :
case OneDriveLinkType . Read :
2018-04-12 13:45:12 +12:00
linkTypeValue = "view" ;
2014-12-26 10:23:42 +13:00
break ;
case OneDriveLinkType . Edit :
2018-04-12 13:45:12 +12:00
linkTypeValue = "edit" ;
2014-12-26 10:23:42 +13:00
break ;
}
2018-04-12 13:45:12 +12:00
string json = JsonConvert . SerializeObject ( new
{
type = linkTypeValue
} ) ;
2018-10-18 05:06:06 +13:00
string response = SendRequest ( HttpMethod . POST , $"https://graph.microsoft.com/v1.0/me/drive/items/{id}/createLink" , json , UploadHelpers . ContentTypeJSON ,
2018-04-12 13:45:12 +12:00
headers : GetAuthHeaders ( ) ) ;
2014-12-26 10:23:42 +13:00
2018-04-14 17:32:48 +12:00
OneDrivePermission permissionInfo = JsonConvert . DeserializeObject < OneDrivePermission > ( response ) ;
2014-12-26 10:23:42 +13:00
2018-04-12 13:45:12 +12:00
if ( permissionInfo ! = null & & permissionInfo . link ! = null )
2014-12-26 10:23:42 +13:00
{
2018-04-12 13:45:12 +12:00
return permissionInfo . link . webUrl ;
2014-12-26 10:23:42 +13:00
}
return null ;
}
2018-04-14 17:32:48 +12:00
public OneDriveFileList GetPathInfo ( string id )
2014-12-26 09:01:22 +13:00
{
2014-12-27 02:58:38 +13:00
if ( ! CheckAuthorization ( ) ) return null ;
2018-04-12 13:45:12 +12:00
string folderPath = GetFolderUrl ( id ) ;
2014-12-26 10:23:42 +13:00
2018-04-12 13:45:12 +12:00
Dictionary < string , string > args = new Dictionary < string , string > ( ) ;
args . Add ( "select" , "id,name" ) ;
args . Add ( "filter" , "folder ne null" ) ;
2014-12-27 12:23:50 +13:00
2018-04-12 13:45:12 +12:00
string response = SendRequest ( HttpMethod . GET , $"https://graph.microsoft.com/v1.0/{folderPath}/children" , args , GetAuthHeaders ( ) ) ;
2014-12-26 19:17:38 +13:00
if ( response ! = null )
{
2018-04-14 17:32:48 +12:00
OneDriveFileList pathInfo = JsonConvert . DeserializeObject < OneDriveFileList > ( response ) ;
2014-12-26 19:17:38 +13:00
return pathInfo ;
}
return null ;
2014-12-26 10:23:42 +13:00
}
}
2014-12-26 19:17:38 +13:00
public class OneDriveFileInfo
{
public string id { get ; set ; }
public string name { get ; set ; }
2018-04-12 13:45:12 +12:00
public string webUrl { get ; set ; }
}
2018-04-14 17:32:48 +12:00
public class OneDrivePermission
2018-04-12 13:45:12 +12:00
{
2018-04-14 17:32:48 +12:00
public OneDriveShareableLink link { get ; set ; }
2014-12-26 19:17:38 +13:00
}
2018-04-14 17:32:48 +12:00
public class OneDriveShareableLink
2014-12-26 19:17:38 +13:00
{
2018-04-12 13:45:12 +12:00
public string webUrl { get ; set ; }
public string webHtml { get ; set ; }
2014-12-26 19:17:38 +13:00
}
2018-04-14 17:32:48 +12:00
public class OneDriveFileList
2014-12-26 19:17:38 +13:00
{
2018-04-12 13:45:12 +12:00
public OneDriveFileInfo [ ] value { get ; set ; }
2014-12-26 19:17:38 +13:00
}
2018-04-14 17:32:48 +12:00
public class OneDriveUploadSession
{
public string uploadUrl { get ; set ; }
public string [ ] nextExpectedRanges { get ; set ; }
}
2014-12-26 10:23:42 +13:00
public enum OneDriveLinkType
{
[Description("An embedded link, which is an HTML code snippet that you can insert into a webpage to provide an interactive view of the corresponding file.")]
Embed ,
[Description("A read-only link, which is a link to a read-only version of the folder or file.")]
Read ,
[Description("A read-write link, which is a link to a read-write version of the folder or file.")]
Edit
2014-12-26 07:36:15 +13:00
}
}