[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: chenxuuu\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: build\n\non:\n  push:\n    paths:\n      - 'maildisk/**'\n      - '.github/workflows/build.yml'\n  pull_request:\n    paths:\n      - 'maildisk/**'\n  workflow_dispatch:\n    inputs:\n      logLevel:\n        description: 'Log level'\n        required: true\n        default: 'warning'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v1\n      - name: environment prepare\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y apt-transport-https\n          sudo apt-get update\n          sudo apt-get install -y dotnet-sdk-5.0\n          sudo apt-get install -y p7zip-full\n      - name: build\n        run: |\n          cd maildisk\n          dotnet publish -r win-x86 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r win-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r win-arm -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r win-arm64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r linux-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r linux-arm -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r linux-arm64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n          dotnet publish -r osx-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true\n      - name: create packages\n        run: |\n          mkdir maildisk-pkg\n          mv maildisk/maildisk/bin/Debug/net5.0/win-x86/publish/* maildisk-pkg/\n          7z a win-x86.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/win-x64/publish/* maildisk-pkg/\n          7z a win-x64.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/win-arm/publish/* maildisk-pkg/\n          7z a win-arm.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/win-arm64/publish/* maildisk-pkg/\n          7z a win-arm64.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/linux-x64/publish/* maildisk-pkg/\n          7z a linux-x64.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/linux-arm/publish/* maildisk-pkg/\n          7z a linux-arm.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/linux-arm64/publish/* maildisk-pkg/\n          7z a linux-arm64.7z maildisk-pkg/*\n          rm maildisk-pkg/*\n          mv maildisk/maildisk/bin/Debug/net5.0/osx-x64/publish/* maildisk-pkg/\n          7z a osx-x64.7z maildisk-pkg/*\n      - uses: actions/upload-artifact@v2\n        with:\n          name: artifact\n          path: |\n            win-x86.7z\n            win-x64.7z\n            win-arm.7z\n            win-arm64.7z\n            linux-x64.7z\n            linux-arm.7z\n            linux-arm64.7z\n            osx-x64.7z\n"
  },
  {
    "path": ".github/workflows/buildGUI.yml",
    "content": "name: Build GUI client\n\non:\n  push:\n    paths:\n      - 'MailDisk-GUI/**'\n      - '.github/workflows/buildGUI.yml'\n  pull_request:\n    paths:\n      - 'MailDisk-GUI/**'\n  workflow_dispatch:\n    inputs:\n      logLevel:\n        description: 'Log level'\n        required: true\n        default: 'warning'\n\njobs:\n  build:\n    runs-on: windows-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n      - name: Add msbuild to PATH\n        uses: microsoft/setup-msbuild@v1.0.2\n      - name: Build\n        run: |\n          cd MailDisk-GUI\n          nuget restore\n          msbuild MailDisk-GUI.sln /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile\n      - name: Upload Artifact\n        uses: actions/upload-artifact@v2\n        with:\n          name: MailDisk-GUI\n          path: MailDisk-GUI/MailDisk-GUI/bin/Release\n"
  },
  {
    "path": ".gitignore",
    "content": "# Created by https://www.gitignore.io/api/visualstudio\r\n\r\n### VisualStudio ###\r\n## Ignore Visual Studio temporary files, build results, and\r\n## files generated by popular Visual Studio add-ons.\r\n##\r\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\r\n\r\n# User-specific files\r\n*.suo\r\n*.user\r\n*.userosscache\r\n*.sln.docstates\r\n\r\n# User-specific files (MonoDevelop/Xamarin Studio)\r\n*.userprefs\r\n\r\n# Build results\r\n[Dd]ebug/\r\n[Dd]ebugPublic/\r\n[Rr]elease/\r\n[Rr]eleases/\r\nx64/\r\nx86/\r\nbld/\r\n[Bb]in/\r\n[Oo]bj/\r\n[Ll]og/\r\n\r\n# Visual Studio 2015 cache/options directory\r\n.vs/\r\n# Uncomment if you have tasks that create the project's static files in wwwroot\r\n#wwwroot/\r\n\r\n# MSTest test Results\r\n[Tt]est[Rr]esult*/\r\n[Bb]uild[Ll]og.*\r\n\r\n# NUNIT\r\n*.VisualState.xml\r\nTestResult.xml\r\n\r\n# Build Results of an ATL Project\r\n[Dd]ebugPS/\r\n[Rr]eleasePS/\r\ndlldata.c\r\n\r\n# .NET Core\r\nproject.lock.json\r\nproject.fragment.lock.json\r\nartifacts/\r\n**/Properties/launchSettings.json\r\n\r\n*_i.c\r\n*_p.c\r\n*_i.h\r\n*.ilk\r\n*.meta\r\n*.obj\r\n*.pch\r\n*.pdb\r\n*.pgc\r\n*.pgd\r\n*.rsp\r\n*.sbr\r\n*.tlb\r\n*.tli\r\n*.tlh\r\n*.tmp\r\n*.tmp_proj\r\n*.log\r\n*.vspscc\r\n*.vssscc\r\n.builds\r\n*.pidb\r\n*.svclog\r\n*.scc\r\n\r\n# Chutzpah Test files\r\n_Chutzpah*\r\n\r\n# Visual C++ cache files\r\nipch/\r\n*.aps\r\n*.ncb\r\n*.opendb\r\n*.opensdf\r\n*.sdf\r\n*.cachefile\r\n*.VC.db\r\n*.VC.VC.opendb\r\n\r\n# Visual Studio profiler\r\n*.psess\r\n*.vsp\r\n*.vspx\r\n*.sap\r\n\r\n# TFS 2012 Local Workspace\r\n$tf/\r\n\r\n# Guidance Automation Toolkit\r\n*.gpState\r\n\r\n# ReSharper is a .NET coding add-in\r\n_ReSharper*/\r\n*.[Rr]e[Ss]harper\r\n*.DotSettings.user\r\n\r\n# JustCode is a .NET coding add-in\r\n.JustCode\r\n\r\n# TeamCity is a build add-in\r\n_TeamCity*\r\n\r\n# DotCover is a Code Coverage Tool\r\n*.dotCover\r\n\r\n# Visual Studio code coverage results\r\n*.coverage\r\n*.coveragexml\r\n\r\n# NCrunch\r\n_NCrunch_*\r\n.*crunch*.local.xml\r\nnCrunchTemp_*\r\n\r\n# MightyMoose\r\n*.mm.*\r\nAutoTest.Net/\r\n\r\n# Web workbench (sass)\r\n.sass-cache/\r\n\r\n# Installshield output folder\r\n[Ee]xpress/\r\n\r\n# DocProject is a documentation generator add-in\r\nDocProject/buildhelp/\r\nDocProject/Help/*.HxT\r\nDocProject/Help/*.HxC\r\nDocProject/Help/*.hhc\r\nDocProject/Help/*.hhk\r\nDocProject/Help/*.hhp\r\nDocProject/Help/Html2\r\nDocProject/Help/html\r\n\r\n# Click-Once directory\r\npublish/\r\n\r\n# Publish Web Output\r\n*.[Pp]ublish.xml\r\n*.azurePubxml\r\n# TODO: Comment the next line if you want to checkin your web deploy settings\r\n# but database connection strings (with potential passwords) will be unencrypted\r\n*.pubxml\r\n*.publishproj\r\n\r\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\r\n# checkin your Azure Web App publish settings, but sensitive information contained\r\n# in these scripts will be unencrypted\r\nPublishScripts/\r\n\r\n# NuGet Packages\r\n*.nupkg\r\n# The packages folder can be ignored because of Package Restore\r\n**/packages/*\r\n# except build/, which is used as an MSBuild target.\r\n!**/packages/build/\r\n# Uncomment if necessary however generally it will be regenerated when needed\r\n#!**/packages/repositories.config\r\n# NuGet v3's project.json files produces more ignorable files\r\n*.nuget.props\r\n*.nuget.targets\r\n\r\n# Microsoft Azure Build Output\r\ncsx/\r\n*.build.csdef\r\n\r\n# Microsoft Azure Emulator\r\necf/\r\nrcf/\r\n\r\n# Windows Store app package directories and files\r\nAppPackages/\r\nBundleArtifacts/\r\nPackage.StoreAssociation.xml\r\n_pkginfo.txt\r\n\r\n# Visual Studio cache files\r\n# files ending in .cache can be ignored\r\n*.[Cc]ache\r\n# but keep track of directories ending in .cache\r\n!*.[Cc]ache/\r\n\r\n# Others\r\nClientBin/\r\n~$*\r\n*~\r\n*.dbmdl\r\n*.dbproj.schemaview\r\n*.jfm\r\n*.pfx\r\n*.publishsettings\r\norleans.codegen.cs\r\n\r\n# Since there are multiple workflows, uncomment next line to ignore bower_components\r\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\r\n#bower_components/\r\n\r\n# RIA/Silverlight projects\r\nGenerated_Code/\r\n\r\n# Backup & report files from converting an old project file\r\n# to a newer Visual Studio version. Backup files are not needed,\r\n# because we have git ;-)\r\n_UpgradeReport_Files/\r\nBackup*/\r\nUpgradeLog*.XML\r\nUpgradeLog*.htm\r\n\r\n# SQL Server files\r\n*.mdf\r\n*.ldf\r\n\r\n# Business Intelligence projects\r\n*.rdl.data\r\n*.bim.layout\r\n*.bim_*.settings\r\n\r\n# Microsoft Fakes\r\nFakesAssemblies/\r\n\r\n# GhostDoc plugin setting file\r\n*.GhostDoc.xml\r\n\r\n# Node.js Tools for Visual Studio\r\n.ntvs_analysis.dat\r\nnode_modules/\r\n\r\n# Typescript v1 declaration files\r\ntypings/\r\n\r\n# Visual Studio 6 build log\r\n*.plg\r\n\r\n# Visual Studio 6 workspace options file\r\n*.opt\r\n\r\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\r\n*.vbw\r\n\r\n# Visual Studio LightSwitch build output\r\n**/*.HTMLClient/GeneratedArtifacts\r\n**/*.DesktopClient/GeneratedArtifacts\r\n**/*.DesktopClient/ModelManifest.xml\r\n**/*.Server/GeneratedArtifacts\r\n**/*.Server/ModelManifest.xml\r\n_Pvt_Extensions\r\n\r\n# Paket dependency manager\r\n.paket/paket.exe\r\npaket-files/\r\n\r\n# FAKE - F# Make\r\n.fake/\r\n\r\n# JetBrains Rider\r\n.idea/\r\n*.sln.iml\r\n\r\n# CodeRush\r\n.cr/\r\n\r\n# Python Tools for Visual Studio (PTVS)\r\n__pycache__/\r\n*.pyc\r\n\r\n# Cake - Uncomment if you are using it\r\n# tools/**\r\n# !tools/packages.config\r\n\r\n# End of https://www.gitignore.io/api/visualstudio"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"{}\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n   Copyright 2017 晨旭\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n   \n   \nThe MIT License\n\nCopyright (c) 2012-2014 Torben Könke\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.2\" />\n    </startup>\n</configuration>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/App.xaml",
    "content": "<Application x:Class=\"MailDisk_GUI.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:MailDisk_GUI\"\n             StartupUri=\"MainWindow.xaml\">\n    <Application.Resources>\n        <ResourceDictionary Source=\"pack://application:,,,/languages/zh-cn.xaml\"/>\n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace MailDisk_GUI\n{\n    /// <summary>\n    /// App.xaml 的交互逻辑\n    /// </summary>\n    public partial class App : Application\n    {\n    }\n}\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/FodyWeavers.xml",
    "content": "﻿<Weavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"FodyWeavers.xsd\">\n  <PropertyChanged />\n  <Costura />\n</Weavers>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/FodyWeavers.xsd",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->\n  <xs:element name=\"Weavers\">\n    <xs:complexType>\n      <xs:all>\n        <xs:element name=\"PropertyChanged\" minOccurs=\"0\" maxOccurs=\"1\">\n          <xs:complexType>\n            <xs:attribute name=\"InjectOnPropertyNameChanged\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"TriggerDependentProperties\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"EnableIsChangedProperty\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"EventInvokerNames\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"CheckForEquality\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"CheckForEqualityUsingBaseEquals\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"UseStaticEqualsFromBase\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"SuppressWarnings\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"SuppressOnPropertyNameChangedWarning\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"Costura\" minOccurs=\"0\" maxOccurs=\"1\">\n          <xs:complexType>\n            <xs:all>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"ExcludeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of assembly names to exclude from the default action of \"embed all Copy Local references\", delimited with line breaks</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"IncludeAssemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of assembly names to include from the default action of \"embed all Copy Local references\", delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"Unmanaged32Assemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"Unmanaged64Assemblies\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n              <xs:element minOccurs=\"0\" maxOccurs=\"1\" name=\"PreloadOrder\" type=\"xs:string\">\n                <xs:annotation>\n                  <xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>\n                </xs:annotation>\n              </xs:element>\n            </xs:all>\n            <xs:attribute name=\"CreateTemporaryAssemblies\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeDebugSymbols\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"DisableCompression\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"DisableCleanup\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"LoadAtModuleInit\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IgnoreSatelliteAssemblies\" type=\"xs:boolean\">\n              <xs:annotation>\n                <xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"ExcludeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of assembly names to exclude from the default action of \"embed all Copy Local references\", delimited with |</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"IncludeAssemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of assembly names to include from the default action of \"embed all Copy Local references\", delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"Unmanaged32Assemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"Unmanaged64Assemblies\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n            <xs:attribute name=\"PreloadOrder\" type=\"xs:string\">\n              <xs:annotation>\n                <xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>\n              </xs:annotation>\n            </xs:attribute>\n          </xs:complexType>\n        </xs:element>\n      </xs:all>\n      <xs:attribute name=\"VerifyAssembly\" type=\"xs:boolean\">\n        <xs:annotation>\n          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"VerifyIgnoreCodes\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"GenerateXsd\" type=\"xs:boolean\">\n        <xs:annotation>\n          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n</xs:schema>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/MailDisk-GUI.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\packages\\PropertyChanged.Fody.3.3.1\\build\\PropertyChanged.Fody.props\" Condition=\"Exists('..\\packages\\PropertyChanged.Fody.3.3.1\\build\\PropertyChanged.Fody.props')\" />\n  <Import Project=\"..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props\" Condition=\"Exists('..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props')\" />\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{B21FA876-FD48-42F0-8B2B-A08B24671DB9}</ProjectGuid>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>MailDisk_GUI</RootNamespace>\n    <AssemblyName>MailDisk-GUI</AssemblyName>\n    <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <WarningLevel>4</WarningLevel>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <NuGetPackageImportStamp>\n    </NuGetPackageImportStamp>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>mailico.ico</ApplicationIcon>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Costura.Fody.4.1.0\\lib\\net40\\Costura.dll</HintPath>\n    </Reference>\n    <Reference Include=\"FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\FontAwesome.WPF.4.7.0.9\\lib\\net40\\FontAwesome.WPF.dll</HintPath>\n    </Reference>\n    <Reference Include=\"Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\Newtonsoft.Json.12.0.3\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\n    </Reference>\n    <Reference Include=\"PropertyChanged, Version=3.3.1.0, Culture=neutral, PublicKeyToken=ee3ee20bcf148ddd, processorArchitecture=MSIL\">\n      <HintPath>..\\packages\\PropertyChanged.Fody.3.3.1\\lib\\net40\\PropertyChanged.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xaml\">\n      <RequiredTargetFramework>4.0</RequiredTargetFramework>\n    </Reference>\n    <Reference Include=\"WindowsBase\" />\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ApplicationDefinition Include=\"App.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </ApplicationDefinition>\n    <Page Include=\"languages\\en-US.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Page Include=\"languages\\zh-CN.xaml\">\n      <SubType>Designer</SubType>\n      <Generator>MSBuild:Compile</Generator>\n    </Page>\n    <Page Include=\"MainWindow.xaml\">\n      <Generator>MSBuild:Compile</Generator>\n      <SubType>Designer</SubType>\n    </Page>\n    <Compile Include=\"App.xaml.cs\">\n      <DependentUpon>App.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"MainWindow.xaml.cs\">\n      <DependentUpon>MainWindow.xaml</DependentUpon>\n      <SubType>Code</SubType>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\n      <SubType>Code</SubType>\n    </Compile>\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DesignTime>True</DesignTime>\n      <DependentUpon>Resources.resx</DependentUpon>\n    </Compile>\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n    </Compile>\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\n      <Generator>ResXFileCodeGenerator</Generator>\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <None Include=\"packages.config\" />\n    <None Include=\"Properties\\Settings.settings\">\n      <Generator>SettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"mailico.ico\" />\n  </ItemGroup>\n  <ItemGroup />\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <Target Name=\"EnsureNuGetPackageBuildImports\" BeforeTargets=\"PrepareForBuild\">\n    <PropertyGroup>\n      <ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息，请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>\n    </PropertyGroup>\n    <Error Condition=\"!Exists('..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Costura.Fody.4.1.0\\build\\Costura.Fody.props'))\" />\n    <Error Condition=\"!Exists('..\\packages\\Fody.6.3.0\\build\\Fody.targets')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\Fody.6.3.0\\build\\Fody.targets'))\" />\n    <Error Condition=\"!Exists('..\\packages\\PropertyChanged.Fody.3.3.1\\build\\PropertyChanged.Fody.props')\" Text=\"$([System.String]::Format('$(ErrorText)', '..\\packages\\PropertyChanged.Fody.3.3.1\\build\\PropertyChanged.Fody.props'))\" />\n  </Target>\n  <Import Project=\"..\\packages\\Fody.6.3.0\\build\\Fody.targets\" Condition=\"Exists('..\\packages\\Fody.6.3.0\\build\\Fody.targets')\" />\n  <PropertyGroup>\n    <LangVersion>9.0</LangVersion>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/MainWindow.xaml",
    "content": "<Window x:Class=\"MailDisk_GUI.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:MailDisk_GUI\"\n        xmlns:fa=\"http://schemas.fontawesome.io/icons/\"\n        mc:Ignorable=\"d\"\n        Title=\"{DynamicResource AppName}\" Height=\"450\" Width=\"800\">\n    <Window.Resources>\n        <Style x:Key=\"TabControlStyle\" TargetType=\"{x:Type TabControl}\">\n            <Setter Property=\"Padding\" Value=\"2\"/>\n            <Setter Property=\"HorizontalContentAlignment\" Value=\"Center\"/>\n            <Setter Property=\"VerticalContentAlignment\" Value=\"Center\"/>\n            <Setter Property=\"Background\" Value=\"White\"/>\n            <Setter Property=\"BorderBrush\" Value=\"#FFACACAC\"/>\n            <Setter Property=\"BorderThickness\" Value=\"1\"/>\n            <Setter Property=\"Foreground\" Value=\"{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}\"/>\n            <Setter Property=\"Template\">\n                <Setter.Value>\n                    <ControlTemplate TargetType=\"{x:Type TabControl}\">\n                        <Grid x:Name=\"templateRoot\" ClipToBounds=\"True\" SnapsToDevicePixels=\"True\" KeyboardNavigation.TabNavigation=\"Local\">\n                            <Grid.ColumnDefinitions>\n                                <ColumnDefinition x:Name=\"ColumnDefinition0\"/>\n                                <ColumnDefinition x:Name=\"ColumnDefinition1\" Width=\"0\"/>\n                            </Grid.ColumnDefinitions>\n                            <Grid.RowDefinitions>\n                                <RowDefinition x:Name=\"RowDefinition0\" Height=\"Auto\"/>\n                                <RowDefinition x:Name=\"RowDefinition1\" Height=\"*\"/>\n                            </Grid.RowDefinitions>\n                            <UniformGrid x:Name=\"HeaderPanel\" Rows=\"1\" Background=\"Transparent\" Grid.Column=\"0\" IsItemsHost=\"True\" Margin=\"0\" Grid.Row=\"0\" KeyboardNavigation.TabIndex=\"1\" Panel.ZIndex=\"1\"/>\n                            <Line X1=\"0\" X2=\"{Binding ActualWidth, RelativeSource={RelativeSource Self}}\" Stroke=\"White\" StrokeThickness=\"0.1\" VerticalAlignment=\"Bottom\" Margin=\"0 0 0 1\" SnapsToDevicePixels=\"True\"/>\n                            <Border x:Name=\"ContentPanel\" BorderBrush=\"{TemplateBinding BorderBrush}\" BorderThickness=\"{TemplateBinding BorderThickness}\" Background=\"{TemplateBinding Background}\" Grid.Column=\"0\" KeyboardNavigation.DirectionalNavigation=\"Contained\" Grid.Row=\"1\" KeyboardNavigation.TabIndex=\"2\" KeyboardNavigation.TabNavigation=\"Local\">\n                                <ContentPresenter x:Name=\"PART_SelectedContentHost\" ContentTemplate=\"{TemplateBinding SelectedContentTemplate}\" Content=\"{TemplateBinding SelectedContent}\" ContentStringFormat=\"{TemplateBinding SelectedContentStringFormat}\" ContentSource=\"SelectedContent\" Margin=\"0\" SnapsToDevicePixels=\"{TemplateBinding SnapsToDevicePixels}\"/>\n                            </Border>\n                        </Grid>\n                        <ControlTemplate.Triggers>\n                            <Trigger Property=\"TabStripPlacement\" Value=\"Bottom\">\n                                <Setter Property=\"Grid.Row\" TargetName=\"HeaderPanel\" Value=\"1\"/>\n                                <Setter Property=\"Grid.Row\" TargetName=\"ContentPanel\" Value=\"0\"/>\n                                <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\"/>\n                                <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"Auto\"/>\n                            </Trigger>\n                            <Trigger Property=\"TabStripPlacement\" Value=\"Left\">\n                                <Setter Property=\"Grid.Row\" TargetName=\"HeaderPanel\" Value=\"0\"/>\n                                <Setter Property=\"Grid.Row\" TargetName=\"ContentPanel\" Value=\"0\"/>\n                                <Setter Property=\"Grid.Column\" TargetName=\"HeaderPanel\" Value=\"0\"/>\n                                <Setter Property=\"Grid.Column\" TargetName=\"ContentPanel\" Value=\"1\"/>\n                                <Setter Property=\"Width\" TargetName=\"ColumnDefinition0\" Value=\"Auto\"/>\n                                <Setter Property=\"Width\" TargetName=\"ColumnDefinition1\" Value=\"*\"/>\n                                <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\"/>\n                                <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"0\"/>\n                            </Trigger>\n                            <Trigger Property=\"TabStripPlacement\" Value=\"Right\">\n                                <Setter Property=\"Grid.Row\" TargetName=\"HeaderPanel\" Value=\"0\"/>\n                                <Setter Property=\"Grid.Row\" TargetName=\"ContentPanel\" Value=\"0\"/>\n                                <Setter Property=\"Grid.Column\" TargetName=\"HeaderPanel\" Value=\"1\"/>\n                                <Setter Property=\"Grid.Column\" TargetName=\"ContentPanel\" Value=\"0\"/>\n                                <Setter Property=\"Width\" TargetName=\"ColumnDefinition0\" Value=\"*\"/>\n                                <Setter Property=\"Width\" TargetName=\"ColumnDefinition1\" Value=\"Auto\"/>\n                                <Setter Property=\"Height\" TargetName=\"RowDefinition0\" Value=\"*\"/>\n                                <Setter Property=\"Height\" TargetName=\"RowDefinition1\" Value=\"0\"/>\n                            </Trigger>\n                            <Trigger Property=\"IsEnabled\" Value=\"False\">\n                                <Setter Property=\"TextElement.Foreground\" TargetName=\"templateRoot\" Value=\"{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}\"/>\n                            </Trigger>\n                        </ControlTemplate.Triggers>\n                    </ControlTemplate>\n                </Setter.Value>\n            </Setter>\n        </Style>\n    </Window.Resources>\n    <TabControl Style=\"{StaticResource TabControlStyle}\">\n        <TabItem>\n            <TabItem.Header>\n                <WrapPanel>\n                    <Grid>\n                        <fa:FontAwesome Icon=\"FolderOpen\" Foreground=\"Yellow\" VerticalAlignment=\"Center\"/>\n                        <fa:FontAwesome Icon=\"FolderOutlinepenOutline\" Foreground=\"Black\" VerticalAlignment=\"Center\" FontWeight=\"Bold\"/>\n                    </Grid>\n                    <TextBlock Text=\"{DynamicResource Files}\" FontSize=\"18\" VerticalAlignment=\"Center\" Margin=\"5,0,0,0\"/>\n                </WrapPanel>\n            </TabItem.Header>\n        </TabItem>\n        <TabItem>\n            <TabItem.Header>\n                <WrapPanel>\n                    <Grid>\n                        <fa:FontAwesome Icon=\"CloudUpload\" Foreground=\"Blue\" VerticalAlignment=\"Center\"/>\n                    </Grid>\n                    <TextBlock Text=\"{DynamicResource Jobs}\" FontSize=\"18\" VerticalAlignment=\"Center\" Margin=\"5,0,0,0\"/>\n                </WrapPanel>\n            </TabItem.Header>\n        </TabItem>\n        <TabItem>\n            <TabItem.Header>\n                <WrapPanel>\n                    <Grid>\n                        <fa:FontAwesome Icon=\"Gears\" Foreground=\"Green\" VerticalAlignment=\"Center\"/>\n                    </Grid>\n                    <TextBlock Text=\"{DynamicResource Settings}\" FontSize=\"18\" VerticalAlignment=\"Center\" Margin=\"5,0,0,0\"/>\n                </WrapPanel>\n            </TabItem.Header>\n        </TabItem>\n        <TabItem>\n            <TabItem.Header>\n                <WrapPanel>\n                    <Grid>\n                        <fa:FontAwesome Icon=\"InfoCircle\" Foreground=\"Gray\" VerticalAlignment=\"Center\"/>\n                    </Grid>\n                    <TextBlock Text=\"{DynamicResource About}\" FontSize=\"18\" VerticalAlignment=\"Center\" Margin=\"5,0,0,0\"/>\n                </WrapPanel>\n            </TabItem.Header>\n        </TabItem>\n    </TabControl>\n</Window>\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/MainWindow.xaml.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace MailDisk_GUI\n{\n    /// <summary>\n    /// MainWindow.xaml 的交互逻辑\n    /// </summary>\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/Properties/AssemblyInfo.cs",
    "content": "using System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Windows;\n\n// 有关程序集的一般信息由以下\n// 控制。更改这些特性值可修改\n// 与程序集关联的信息。\n[assembly: AssemblyTitle(\"MailDisk-GUI\")]\n[assembly: AssemblyDescription(\"邮箱网盘\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"chenxublog.com\")]\n[assembly: AssemblyProduct(\"MailDisk-GUI\")]\n[assembly: AssemblyCopyright(\"Copyright © chenxuuu\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// 将 ComVisible 设置为 false 会使此程序集中的类型\n//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型\n//请将此类型的 ComVisible 特性设置为 true。\n[assembly: ComVisible(false)]\n\n//若要开始生成可本地化的应用程序，请设置\n//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>\n//例如，如果您在源文件中使用的是美国英语，\n//使用的是美国英语，请将 <UICulture> 设置为 en-US。  然后取消\n//对以下 NeutralResourceLanguage 特性的注释。  更新\n//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。\n\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\n\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //主题特定资源词典所处位置\n                                     //(未在页面中找到资源时使用，\n                                     //或应用程序资源字典中找到时使用)\n    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置\n                                              //(未在页面中找到资源时使用，\n                                              //、应用程序或任何主题专用资源字典中找到时使用)\n)]\n\n\n// 程序集的版本信息由下列四个值组成: \n//\n//      主版本\n//      次版本\n//      生成号\n//      修订号\n//\n//可以指定所有这些值，也可以使用“生成号”和“修订号”的默认值\n//通过使用 \"*\"，如下所示:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     此代码由工具生成。\n//     运行时版本: 4.0.30319.42000\n//\n//     对此文件的更改可能导致不正确的行为，如果\n//     重新生成代码，则所做更改将丢失。\n// </auto-generated>\n//------------------------------------------------------------------------------\n\n\nnamespace MailDisk_GUI.Properties\n{\n    /// <summary>\n    ///   强类型资源类，用于查找本地化字符串等。\n    /// </summary>\n    // 此类是由 StronglyTypedResourceBuilder\n    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。\n    // 若要添加或删除成员，请编辑 .ResX 文件，然后重新运行 ResGen\n    // (以 /str 作为命令选项)，或重新生成 VS 项目。\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"4.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources\n    {\n\n        private static global::System.Resources.ResourceManager resourceMan;\n\n        private static global::System.Globalization.CultureInfo resourceCulture;\n\n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources()\n        {\n        }\n\n        /// <summary>\n        ///   返回此类使用的缓存 ResourceManager 实例。\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager\n        {\n            get\n            {\n                if ((resourceMan == null))\n                {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"MailDisk_GUI.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n\n        /// <summary>\n        ///   重写当前线程的 CurrentUICulture 属性，对\n        ///   使用此强类型资源类的所有资源查找执行重写。\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture\n        {\n            get\n            {\n                return resourceCulture;\n            }\n            set\n            {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\n\nnamespace MailDisk_GUI.Properties\n{\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"11.0.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase\n    {\n\n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n\n        public static Settings Default\n        {\n            get\n            {\n                return defaultInstance;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    <Profile Name=\"(Default)\" />\n  </Profiles>\n  <Settings />\n</SettingsFile>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/languages/en-US.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:MailDisk_GUI.languages\" \n                    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n    <system:String x:Key=\"AppName\">MailDisk</system:String>\n    <system:String x:Key=\"Files\">Files</system:String>\n    <system:String x:Key=\"Jobs\">Jobs</system:String>\n    <system:String x:Key=\"Settings\">Settings</system:String>\n    <system:String x:Key=\"About\">About</system:String>\n</ResourceDictionary>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/languages/zh-CN.xaml",
    "content": "<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n                    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n                    xmlns:local=\"clr-namespace:MailDisk_GUI.languages\" \n                    xmlns:system=\"clr-namespace:System;assembly=mscorlib\">\n    <system:String x:Key=\"AppName\">邮箱网盘</system:String>\n    <system:String x:Key=\"Files\">文件</system:String>\n    <system:String x:Key=\"Jobs\">任务</system:String>\n    <system:String x:Key=\"Settings\">设置</system:String>\n    <system:String x:Key=\"About\">关于</system:String>\n</ResourceDictionary>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Costura.Fody\" version=\"4.1.0\" targetFramework=\"net462\" />\n  <package id=\"Fody\" version=\"6.3.0\" targetFramework=\"net462\" developmentDependency=\"true\" />\n  <package id=\"FontAwesome.WPF\" version=\"4.7.0.9\" targetFramework=\"net462\" />\n  <package id=\"Newtonsoft.Json\" version=\"13.0.1\" targetFramework=\"net462\" />\n  <package id=\"PropertyChanged.Fody\" version=\"3.3.1\" targetFramework=\"net462\" />\n</packages>"
  },
  {
    "path": "MailDisk-GUI/MailDisk-GUI.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.30717.126\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"MailDisk-GUI\", \"MailDisk-GUI\\MailDisk-GUI.csproj\", \"{B21FA876-FD48-42F0-8B2B-A08B24671DB9}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {99ABBE37-373C-4151-9CC1-8F0A99D7C420}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "# 邮箱网盘\r\n\r\n![build](https://github.com/chenxuuu/Mail-Box-Net-Disk/workflows/build/badge.svg)\r\n![Build GUI client](https://github.com/chenxuuu/Mail-Box-Net-Disk/workflows/Build%20GUI%20client/badge.svg)\r\n\r\n利用邮箱实现网盘功能的工具。\r\n\r\n本项目有两个仓库，不过建议在GitHub进行star/pr操作：\r\n\r\nGitHub：[https://github.com/chenxuuu/Mail-Box-Net-Disk](https://github.com/chenxuuu/Mail-Box-Net-Disk)\r\n\r\ngit.osc：[https://gitee.com/chenxuuu/Mail-Box-Net-Disk](https://gitee.com/chenxuuu/Mail-Box-Net-Disk)\r\n\r\n## 下载\r\n\r\n每次发布版本，会自动在GitHub的release页更新：[点击前往](https://github.com/chenxuuu/Mail-Box-Net-Disk/releases/latest)\r\n\r\n## 原理\r\n\r\n利用邮箱附件作为文件存储空间，实现文件的上传下载功能\r\n\r\n## 功能\r\n\r\n- [x] 支持smtp/imap协议的邮箱\r\n- [x] 读取文件夹列表/新建文件夹\r\n- [x] 上传小于限制大小的文件\r\n- [x] 读取已存在的文件\r\n- [x] 上传大文件自动分卷\r\n- [x] 识别分卷上传的文件，下载自动合并\r\n- [x] 优化文件搜索速度\r\n- [x] 完成命令行工具成品\r\n- [x] 支持文件夹上传\r\n- [x] 支持文件夹下载\r\n- [ ] win系统下的gui管理工具\r\n- [ ] 其他系统下的gui管理工具\r\n\r\n## 命令列表\r\n\r\n```cmd\r\nmaildisk -h 查看命令帮助\r\n\r\n         -s\r\n         更改邮箱参数设置\r\n\r\n         -lf\r\n         列出邮箱的所有邮件文件夹\r\n\r\n         -cf <邮件文件夹名>\r\n         新建一个邮件文件夹\r\n\r\n         -l <邮件文件夹>\r\n         列出所有该邮件文件夹下的文件\r\n\r\n         -c <邮件文件夹>\r\n         清除所有该邮件文件夹下不完整的分卷文件\r\n\r\n         -u <邮件文件夹> <本地文件> <云端文件>\r\n         上传文件\r\n\r\n         -d <邮件文件夹> <本地文件> <云端文件>\r\n         下载文件\r\n\r\n         -uf <邮件文件夹> <本地文件夹> <云端虚拟文件夹路径>\r\n         上传文件夹，并清理不完整分卷的邮件\r\n         注意：如果云端存在该文件，则不会上传\r\n\r\n         -df <邮件文件夹> <本地文件夹> <云端虚拟文件夹路径>\r\n         下载文件夹\r\n\r\n         所有文件和路径都不能包含'<'符号\r\n```\r\n\r\n## 加入该项目\r\n\r\n如果你愿意忍受我写的智障代码，那么欢迎pr\r\n\r\n## 其他\r\n\r\n该项目的代码可以随意引用，但请保留指向该项目的说明文字\r\n"
  },
  {
    "path": "maildisk/maildisk/Program.cs",
    "content": "using MailKit;\r\nusing MailKit.Net.Imap;\r\nusing MailKit.Search;\r\nusing System;\r\nusing maildisk.apis;\r\nusing System.IO;\r\nusing System.Text;\r\n\r\nnamespace maildisk\r\n{\r\n    class Program\r\n    {\r\n        static void Main(string[] args)\r\n        {\r\n            if(args.Length > 0)\r\n            switch(args[0])\r\n            {\r\n                case \"-h\":\r\n                        Console.WriteLine(@\"\r\n***********\r\n*Mail Disk*\r\n***********\r\nYou can use these commands:\r\n\r\n-h: \r\nshow commands we support.\r\n\r\n-s:\r\nset your imap settings.\r\n\r\n-lf:\r\nlist all folders on mail server.\r\n\r\n-cf <folder name>:\r\ncreate new folder on mail server.\r\n\r\n-l <email folder>:\r\nshow files in this folder.\r\n\r\n-c <email folder>:\r\nclear all wrong files in this folder.\r\n\r\n-u <email folder> <local file> <file name on cloud>:\r\nupload a file to net disk.\r\n\r\n-d <email folder> <local file> <file name on cloud>:\r\ndownload a file from net disk.\r\n\r\n-uf <email folder> <local folder> <folder name on cloud>:\r\nupload a folder to net disk and clear all wrong files in this email folder.\r\nNotice: if file is exist on cloud, it will not be uploaded.\r\n\r\n-df <email folder> <local folder> <folder name on cloud>:\r\ndownload a folder from net disk.\r\nNotice: local file must be not exist.\r\n\r\nAll file and path should not contain '<'\".Replace(\"\\r\\n\",\"\\r\\n\\t\"));\r\n                        return;\r\n\r\n                    case \"-s\":\r\n                        Settings.Set();\r\n                        return;\r\n\r\n                    case \"-lf\":\r\n                        var lfdisk = Settings.GetDisk();\r\n                        if (lfdisk == null) return;\r\n                        Console.WriteLine(\"getting all folders ...\");\r\n                        var all = lfdisk.GetFolders();\r\n                        Console.WriteLine(\"here's all folders:\");\r\n                        foreach(var f in all)\r\n                        {\r\n                            Console.WriteLine(f.FullName);\r\n                        }\r\n                        return;\r\n\r\n                    case \"-cf\":\r\n                        var cfdisk = Settings.GetDisk();\r\n                        if (cfdisk == null) return;\r\n                        if(args.Length < 2) { Console.WriteLine(\"please enter a folder name\");return; }\r\n                        Console.WriteLine($\"creating folder {args[1]} ...\");\r\n                        cfdisk.CreatFolder(args[1]);\r\n                        return;\r\n\r\n                    case \"-l\":\r\n                        var ldisk = Settings.GetDisk();\r\n                        if (ldisk == null) return;\r\n                        if (args.Length < 2) { Console.WriteLine(\"please enter a folder name\"); return; }\r\n                        Console.WriteLine($\"fetching file list with folder {args[1]} ...\");\r\n                        var lfiles = ldisk.GetFileList(args[1]);\r\n                        Console.WriteLine($\"\\r\\n\\r\\ndone! list of files:\");\r\n                        foreach (var s in lfiles)\r\n                        {\r\n                            Console.WriteLine(s);\r\n                        }\r\n                        return;\r\n\r\n                    case \"-c\":\r\n                        var cdisk = Settings.GetDisk();\r\n                        if (cdisk == null) return;\r\n                        if (args.Length < 2) { Console.WriteLine(\"please enter a folder name\"); return; }\r\n                        Console.WriteLine($\"fetching file list with folder {args[1]} ...\");\r\n                        cdisk.GetFileList(args[1], true);\r\n                        Console.WriteLine($\"\\r\\n\\r\\ndone!\");\r\n                        return;\r\n\r\n                    case \"-u\":\r\n                        var udisk = Settings.GetDisk();\r\n                        if (udisk == null) return;\r\n                        if (args.Length < 4) { Console.WriteLine(\"wrong args count\"); return; }\r\n                        Console.WriteLine($\"uploading file {args[2]} to {args[1]} as {args[3]} ...\");\r\n                        if(args[3].IndexOf(\"<\")>=0)\r\n                        {\r\n                            Console.WriteLine($\"error! file name do not contain '<'\");\r\n                            return;\r\n                        }\r\n                        udisk.UploadBigFile(args[3], args[1], args[2], (int)Settings.maxBlock * 1024 * 1024);\r\n                        return;\r\n\r\n                    case \"-d\":\r\n                        var ddisk = Settings.GetDisk();\r\n                        if (ddisk == null) return;\r\n                        if (args.Length < 4) { Console.WriteLine(\"wrong args count\"); return; }\r\n                        Console.WriteLine($\"Download file {args[3]} from {args[1]} as {args[2]} ...\");\r\n                        if (args[3].IndexOf(\"<\") >= 0)\r\n                        {\r\n                            Console.WriteLine($\"error! file name do not contain '<'\");\r\n                            return;\r\n                        }\r\n                        ddisk.DownloadFile(args[1], args[3], args[2]);\r\n                        return;\r\n\r\n                    case \"-uf\":\r\n                        var ufdisk = Settings.GetDisk();\r\n                        if (ufdisk == null) return;\r\n                        if (args.Length < 4) { Console.WriteLine(\"wrong args count\"); return; }\r\n                        Console.WriteLine($\"upload folder {args[3]} to {args[1]} as {args[2]} ...\");\r\n                        if (args[3].IndexOf(\"<\") >= 0)\r\n                        {\r\n                            Console.WriteLine($\"error! folder name do not contain '<'\");\r\n                            return;\r\n                        }\r\n                        ufdisk.RefreshFiles(args[1]);\r\n                        ufdisk.UploadFolder(args[3], args[1], args[2], (int)Settings.maxBlock * 1024 * 1024);\r\n                        Console.WriteLine(\"done! all files uploaded!\");\r\n                        return;\r\n\r\n                    case \"-df\":\r\n                        var dfdisk = Settings.GetDisk();\r\n                        if (dfdisk == null) return;\r\n                        if (args.Length < 4) { Console.WriteLine(\"wrong args count\"); return; }\r\n                        Console.WriteLine($\"Download folder {args[3]} from {args[1]} as {args[2]} ...\");\r\n                        if (args[3].IndexOf(\"<\") >= 0)\r\n                        {\r\n                            Console.WriteLine($\"error! folder name do not contain '<'\");\r\n                            return;\r\n                        }\r\n                        dfdisk.DownloadFolder(args[3], args[1], args[2]);\r\n                        Console.WriteLine(\"done! all files downloaded!\");\r\n                        return;\r\n\r\n                    default:\r\n                        break;\r\n            }\r\n            Console.WriteLine(@\"no commond matched\r\nuse -h to show commands we support\");\r\n            return;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "maildisk/maildisk/apis/MailClient.cs",
    "content": "﻿using MailKit;\r\nusing MailKit.Net.Imap;\r\nusing MailKit.Search;\r\nusing System;\r\nusing System.Collections;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\n\r\nnamespace maildisk.apis\r\n{\r\n    class MailClient\r\n    {\r\n        private string address;\r\n        private ImapClient client = new ImapClient();\r\n\r\n        /// <summary>\r\n        /// new mail client\r\n        /// </summary>\r\n        /// <param name=\"server\">server address</param>\r\n        /// <param name=\"port\">server port</param>\r\n        /// <param name=\"useSsl\">server use ssl or not</param>\r\n        /// <param name=\"account\">your account</param>\r\n        /// <param name=\"password\">your password</param>\r\n        /// <param name=\"address\">your email address</param>\r\n        public MailClient(string server, int port, bool useSsl, string account, string password, string address)\r\n        {\r\n            client.ServerCertificateValidationCallback = (s, c, h, e) => true;\r\n            client.Connect(server, port, useSsl);\r\n            client.Authenticate(account, password);\r\n            this.address = address;\r\n#if DEBUG\r\n            Console.WriteLine($\"[mail create]mail client created, on {server}:{port}\" +\r\n                $\", ssl:{useSsl}, {account}, {address}\");\r\n#endif\r\n        }\r\n\r\n        /// <summary>\r\n        /// get all folders in this mail\r\n        /// </summary>\r\n        /// <param name=\"path\">path, default is empty</param>\r\n        /// <returns>folder list</returns>\r\n        public IMailFolder[] GetFolders(string path = \"\")\r\n        {\r\n            ArrayList folders = new ArrayList();\r\n            \r\n            var personal = client.GetFolder(path);\r\n\r\n            foreach (var folder in personal.GetSubfolders(false))\r\n            {\r\n                if (folder.GetSubfolders(false).Count > 0)\r\n                {\r\n#if DEBUG\r\n                    Console.WriteLine($\"[folder open] {path + folder.Name}\");\r\n#endif\r\n                    folders.AddRange(GetFolders(folder.FullName));\r\n                }\r\n                else\r\n                {\r\n                    folders.Add(folder);\r\n#if DEBUG\r\n                    Console.WriteLine($\"[folder get] {folder.FullName}\");\r\n#endif\r\n                }\r\n            }\r\n            return (IMailFolder[])folders.ToArray(typeof(IMailFolder));\r\n        }\r\n\r\n        /// <summary>\r\n        /// get one folder\r\n        /// </summary>\r\n        /// <param name=\"path\">path, default is empty</param>\r\n        /// <returns>folder</returns>\r\n        public IMailFolder GetFolder(string path)\r\n        {\r\n            return client.GetFolder(path);\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// get not read mails, and mark as seen\r\n        /// </summary>\r\n        /// <returns>mails' IMessageSummary</returns>\r\n        public IList<IMessageSummary> GetNotSeen()\r\n        {\r\n            ArrayList mails = new ArrayList();\r\n            var inbox = client.Inbox;\r\n            inbox.Open(FolderAccess.ReadWrite);\r\n            var uids = inbox.Search(SearchQuery.NotSeen);\r\n            inbox.AddFlags(uids, MessageFlags.Seen, true);\r\n#if DEBUG\r\n            Console.WriteLine($\"[GetNotSeen] mails count {uids.Count}\");\r\n#endif\r\n            return inbox.Fetch(uids,\r\n                    MessageSummaryItems.Full | MessageSummaryItems.UniqueId);\r\n        }\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "maildisk/maildisk/apis/Settings.cs",
    "content": "using Newtonsoft.Json;\r\nusing Newtonsoft.Json.Linq;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.Text;\r\n\r\nnamespace maildisk.apis\r\n{\r\n    class Settings\r\n    {\r\n        public static long maxBlock = 0;\r\n\r\n        private static string GetBasePath()\r\n        {\r\n            using var processModule = Process.GetCurrentProcess().MainModule;\r\n            return Path.GetDirectoryName(processModule?.FileName);\r\n        }\r\n\r\n        /// <summary>\r\n        /// check setting file exist\r\n        /// </summary>\r\n        /// <returns>file exist</returns>\r\n        public static bool CheckSetings()\r\n        {\r\n            return File.Exists(GetBasePath() + \"/mail.json\");\r\n        }\r\n\r\n        /// <summary>\r\n        /// create settings file\r\n        /// </summary>\r\n        public static void Set()\r\n        {\r\n            Console.WriteLine(\"Notice: If you're using QQMail, pleause create a login code on mail.qq.com\\r\\n\");\r\n            JObject o = new JObject();\r\n            Console.Write(\"Enter the imap server address:\");\r\n            o[\"imap\"] = Console.ReadLine();\r\n\r\n            Console.Write(\"Enter the imap server port(default 993):\");\r\n            try { o[\"port\"] = int.Parse(Console.ReadLine()); }\r\n            catch { o[\"port\"] = 993; }\r\n\r\n            Console.Write(\"Use ssl?(y/n):\");\r\n            o[\"ssl\"] = Console.ReadLine() == \"y\" ? true : false;\r\n\r\n            Console.Write(\"Account:\");\r\n            o[\"account\"] = Console.ReadLine();\r\n\r\n            Console.Write(\"Password:\");\r\n            o[\"password\"] = Console.ReadLine();\r\n\r\n            Console.Write(\"E-mail address:\");\r\n            o[\"address\"] = Console.ReadLine();\r\n\r\n            Console.Write(\"Max size for each file(MiB):\");\r\n            o[\"block\"] = Console.ReadLine();\r\n\r\n            File.WriteAllText(GetBasePath() + \"/mail.json\", o.ToString());\r\n\r\n            Console.WriteLine(\"\\r\\ndone! enjoy!\");\r\n        }\r\n\r\n        /// <summary>\r\n        /// return a VisualDisk\r\n        /// </summary>\r\n        /// <returns>VisualDisk</returns>\r\n        public static VisualDisk GetDisk()\r\n        {\r\n            if (CheckSetings())\r\n            {\r\n                string s = File.ReadAllText(GetBasePath() + \"/mail.json\");\r\n                JObject jo = (JObject)JsonConvert.DeserializeObject(s);\r\n                var disk = new VisualDisk(\r\n                    (string)jo[\"imap\"],\r\n                    (int)jo[\"port\"],\r\n                    (bool)jo[\"ssl\"],\r\n                    (string)jo[\"account\"],\r\n                    (string)jo[\"password\"],\r\n                    (string)jo[\"address\"]);\r\n                maxBlock = (long)jo[\"block\"];\r\n                return disk;\r\n            }\r\n            else\r\n            {\r\n                Console.WriteLine(\"settings not found\\r\\npleause use -s command to set imap settings\");\r\n                return null;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "maildisk/maildisk/apis/VisualDisk.cs",
    "content": "using MailKit;\r\nusing MailKit.Net.Imap;\r\nusing MailKit.Net.Smtp;\r\nusing MailKit.Search;\r\nusing MimeKit;\r\nusing System;\r\nusing System.Collections;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Text.RegularExpressions;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace maildisk.apis\r\n{\r\n    class VisualDisk\r\n    {\r\n        private string address;\r\n        private string account;\r\n        private string password;\r\n        private string imapServer;\r\n        private int imapPort;\r\n        private bool imapSsl;\r\n        private string lastFolder;\r\n\r\n        public VisualDisk(string imapServer, int port, bool useSsl, string account, string password, string address)\r\n        {\r\n            this.imapServer = imapServer;\r\n            this.imapPort = port;\r\n            this.imapSsl = useSsl;\r\n            this.address = address;\r\n            this.account = account;\r\n            this.password = password;\r\n            //auto mark as seen task\r\n            Task.Run(() => {\r\n                while(true)\r\n                {\r\n                    try\r\n                    {\r\n                        if (lastFolder != null)\r\n                        {\r\n                            var client = GetImapClient();\r\n                            var f = client.GetFolder(lastFolder);\r\n                            f.Open(FolderAccess.ReadWrite);\r\n                            var uids = f.Search(SearchQuery.NotSeen);\r\n                            foreach (var u in uids)\r\n                            {\r\n                                f.AddFlags(u, MessageFlags.Seen, true);\r\n\r\n                                Console.WriteLine($\"[disk check]add a seen flag\");\r\n\r\n                            }\r\n                            client.Disconnect(true);\r\n                        }\r\n                    }\r\n                    catch(Exception e)\r\n                    {\r\n                        Console.WriteLine($\"[disk check]fetch error: {e.Message}\");\r\n                    }\r\n                    Task.Delay(30 * 1000).Wait();\r\n                }\r\n            });\r\n        }\r\n\r\n        /// <summary>\r\n        /// get a imap client\r\n        /// </summary>\r\n        /// <returns>imap client</returns>\r\n        public ImapClient GetImapClient()\r\n        {\r\n            ImapClient client = new ImapClient();\r\n            client.ServerCertificateValidationCallback = (s, c, h, e) => true;\r\n            client.Connect(imapServer, imapPort, imapSsl);\r\n            client.Authenticate(account, password);\r\n            //判断是否 添加ID COMMOND命令\r\n            if ((client.Capabilities | ImapCapabilities.Id) == client.Capabilities)\r\n            {\r\n                var clientImplementation = new ImapImplementation\r\n                {\r\n                    Name = \"Foxmail\",\r\n                    Version = \"9.156\"\r\n                };\r\n                var serverImplementation = client.Identify(clientImplementation);\r\n            }\r\n            return client;\r\n        }\r\n\r\n        /// <summary>\r\n        /// upload a file\r\n        /// </summary>\r\n        /// <param name=\"fileName\">file name on cloud disk</param>\r\n        /// <param name=\"folderPath\">folder on email</param>\r\n        /// <param name=\"filePath\">local file path</param>\r\n        /// <returns>file upload success or not</returns>\r\n        private bool Upload(string fileName, string folderPath, Stream file)\r\n        {\r\n\r\n            Console.WriteLine($\"[disk upload]upload {fileName} to mail folder {folderPath}, size:{file.Length}\");\r\n\r\n            var client = GetImapClient();\r\n            var message = new MimeMessage();\r\n            message.From.Add(MailboxAddress.Parse(address));\r\n            message.To.Add(MailboxAddress.Parse(address));\r\n            message.Subject = \"[mailDisk]\" + fileName;\r\n            var body = new TextPart(\"plain\")\r\n            {\r\n                Text =\r\n                \"This mail was send by mail disk\\r\\n\" +\r\n                \"please do not delete\"\r\n            };\r\n\r\n            var attachment = new MimePart()\r\n            {\r\n                Content = new MimeContent(file, ContentEncoding.Default),\r\n                ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),\r\n                ContentTransferEncoding = ContentEncoding.Base64,\r\n                FileName = \"attachment.netdiskfile\"\r\n            };\r\n\r\n            var multipart = new Multipart(\"mixed\");\r\n            multipart.Add(body);\r\n            multipart.Add(attachment);\r\n            message.Body = multipart;\r\n\r\n            Console.WriteLine(\"[disk upload]appending...\");\r\n\r\n            var folder = GetImapClient().GetFolder(folderPath);\r\n            folder.Open(FolderAccess.ReadWrite);\r\n            var uid = folder.Append(message);\r\n            lastFolder = folderPath;\r\n\r\n\r\n            Console.WriteLine($\"[disk upload]upload success\");\r\n\r\n            client.Disconnect(true);\r\n            return true;\r\n        }\r\n\r\n        /// <summary>\r\n        /// get file list with folder\r\n        /// </summary>\r\n        /// <param name=\"folderPath\">folder</param>\r\n        /// <returns>files' name</returns>\r\n        public string[] GetFileList(string folderPath, bool deleteMissing = false)\r\n        {\r\n            ArrayList mails = new ArrayList();\r\n            var client = GetImapClient();\r\n            var folder = client.GetFolder(folderPath);\r\n            folder.Open(FolderAccess.ReadWrite);\r\n            Console.WriteLine($\"find {folder.Count} mails in this folder\");\r\n            for(int i = 0;i < folder.Count; i += 100)\r\n            {\r\n                Console.WriteLine($\"fatching mails {i}/{folder.Count}\");\r\n                int max = i + 100;\r\n                if (max >= folder.Count)\r\n                    max = folder.Count - 1;\r\n                foreach (var m in folder.Fetch(i, max, MessageSummaryItems.Full | MessageSummaryItems.UniqueId))\r\n                {\r\n                    if(m.Envelope.Subject.IndexOf(\"[mailDisk]\") == 0)\r\n                        mails.Add(m.Envelope.Subject.Substring(\"[mailDisk]\".Length));\r\n                }\r\n                if (max == folder.Count)\r\n                    break;\r\n            }\r\n            Console.WriteLine($\"mails in {folder.Count} fatched ok.\");\r\n\r\n            ArrayList files = new ArrayList();\r\n            foreach(string f in mails)\r\n            {\r\n                if (f.IndexOf(\"<\")>=0)\r\n                {\r\n                    MatchCollection mc = Regex.Matches(f, @\"(.+?)<1/(\\d+?)>\");\r\n                    if (mc.Count > 0 && mc[0].Groups.Count == 3)\r\n                    {\r\n                        Console.WriteLine($\"find file {mc[0].Groups[1]} with {mc[0].Groups[2]} parts,checking...\");\r\n                        bool result = true;//check is it have all files\r\n                        int sum = int.Parse(mc[0].Groups[2].ToString());\r\n                        for(int i=1;i<=sum;i++)\r\n                        {\r\n                            if(!mails.Contains($\"{mc[0].Groups[1]}<{i}/{sum}>\"))\r\n                            {\r\n                                result = false;\r\n                                break;\r\n                            }\r\n                        }\r\n                        if(result)\r\n                        {\r\n                            files.Add(mc[0].Groups[1].ToString());\r\n                            Console.WriteLine($\"file {mc[0].Groups[1]} check ok\");\r\n                        }\r\n                        else\r\n                            Console.WriteLine($\"file {mc[0].Groups[1]}'s parts are missing\");\r\n                    }\r\n                        \r\n                }\r\n                else\r\n                {\r\n                    files.Add(f);\r\n                }\r\n            }\r\n            if(deleteMissing)\r\n            {\r\n                Console.WriteLine(\"start cleaning all missing mails\");\r\n                for (int i = 0; i < folder.Count; i += 100)\r\n                {\r\n                    Console.WriteLine($\"fatching mails {i}/{folder.Count}\");\r\n                    int max = i + 100;\r\n                    if (max >= folder.Count)\r\n                        max = folder.Count - 1;\r\n                    foreach (var m in folder.Fetch(i, max, MessageSummaryItems.Full | MessageSummaryItems.UniqueId))\r\n                    {\r\n                        if (m.Envelope.Subject.IndexOf(\"[mailDisk]\") == 0)\r\n                        {\r\n                            string name = m.Envelope.Subject.Substring(\"[mailDisk]\".Length);\r\n                            Console.WriteLine($\"checking file {name}\");\r\n                            MatchCollection mc = Regex.Matches(name, @\"(.+?)<(\\d+?)/(\\d+?)>\");\r\n                            if (mc.Count > 0 && mc[0].Groups.Count == 4)\r\n                                name = mc[0].Groups[1].ToString();\r\n                            if (!files.Contains(name))\r\n                            {\r\n                                Console.WriteLine($\"file {name} is not right, mark as deleted\");\r\n                                folder.AddFlags(m.UniqueId, MessageFlags.Deleted, true);\r\n                            }\r\n                        }\r\n                    }\r\n                    if (max == folder.Count)\r\n                        break;\r\n                }\r\n                folder.Expunge();\r\n            }\r\n            client.Disconnect(true);\r\n            return (string[])files.ToArray(typeof(string));\r\n        }\r\n\r\n        /// <summary>\r\n        /// download file\r\n        /// </summary>\r\n        /// <param name=\"folderPath\">mail folder</param>\r\n        /// <param name=\"fileName\">file save name</param>\r\n        public void DownloadFile(string folderPath, string fileName, string local, \r\n            ImapClient client = null, \r\n            IList<IMessageSummary> all = null,\r\n            IMailFolder folder = null)\r\n        {\r\n            if(File.Exists(local))\r\n            {\r\n                Console.WriteLine($\"error! file {local} already exist!\");\r\n                return;\r\n            }\r\n            if(client == null)\r\n                client = GetImapClient();\r\n\r\n            var invalidFileName = Path.GetInvalidFileNameChars();\r\n            fileName = invalidFileName.Aggregate(fileName, (o, r) => (o.Replace(r.ToString(), string.Empty)));\r\n\r\n            if (all == null)\r\n            {\r\n                folder = client.GetFolder(folderPath);\r\n                folder.Open(FolderAccess.ReadOnly);\r\n                var uids = folder.Search(SearchQuery.SubjectContains($\"[mailDisk]{fileName}\"));\r\n\r\n                Console.WriteLine($\"find {uids.Count} matchs in this folder\");\r\n                Console.WriteLine($\"fatching mails\");\r\n                all = folder.Fetch(uids, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);\r\n            }\r\n            bool singleFile = true;\r\n            int fileSum = 0;\r\n            bool hasFile = false;\r\n            foreach(var m in all)\r\n            {\r\n                string subject = m.Envelope.Subject.Substring(\"[mailDisk]\".Length);\r\n                if (subject.IndexOf(fileName) == 0 && (subject.Length == fileName.Length || subject.Substring(fileName.Length, 1) == \"<\"))\r\n                {\r\n                    if(subject.Length == fileName.Length)\r\n                    {\r\n                        hasFile = true;\r\n                        break;\r\n                    }\r\n                    MatchCollection mc = Regex.Matches(subject, @\"<1/(\\d+?)>\");\r\n                    if (mc.Count > 0 && mc[0].Groups.Count == 2)\r\n                    {\r\n                        fileSum = int.Parse(mc[0].Groups[1].ToString());\r\n                        singleFile = false;\r\n                        hasFile = true;\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n\r\n            if(!hasFile)\r\n            {\r\n                Console.WriteLine($\"error! file not exist!\");\r\n                return;\r\n            }\r\n\r\n            if(singleFile)\r\n            {\r\n                foreach (var m in all)\r\n                {\r\n                    if(m.Envelope.Subject.IndexOf($\"[mailDisk]{fileName}\") == 0)\r\n                    {\r\n                        while(true)\r\n                        {\r\n                            try\r\n                            {\r\n                                using (var output = File.Create(local))\r\n                                {\r\n                                    var r = Download(folder, m.UniqueId);\r\n                                    r.Position = 0;\r\n                                    r.CopyTo(output);\r\n                                    break;\r\n                                }\r\n                            }\r\n                            catch(Exception e)\r\n                            {\r\n                                Console.WriteLine($\"[disk Download]fail, retry, infomation:\" + e.Message);\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            else\r\n            {\r\n                ArrayList mails = new ArrayList();\r\n                foreach (var m in all)\r\n                {\r\n                    string subject = m.Envelope.Subject.Substring(\"[mailDisk]\".Length);\r\n                    mails.Add(subject);\r\n                }\r\n                Console.WriteLine($\"find file {fileName} with {fileSum} parts,checking...\");\r\n                bool result = true;//check is it have all files\r\n                for (int i = 1; i <= fileSum; i++)\r\n                {\r\n                    if (!mails.Contains($\"{fileName}<{i}/{fileSum}>\"))\r\n                    {\r\n                        result = false;\r\n                        break;\r\n                    }\r\n                }\r\n                if (result)\r\n                {\r\n                    Console.WriteLine($\"file {fileName} check ok, begin download...\");\r\n                    using (var output = File.Create(local))\r\n                    {\r\n                        for (int i = 1; i <= fileSum; i++)\r\n                        {\r\n                            foreach (var m in all)\r\n                            {\r\n                                if (m.Envelope.Subject.IndexOf($\"[mailDisk]{fileName}<{i}/{fileSum}>\") == 0)\r\n                                {\r\n                                    while (true)\r\n                                    {\r\n                                        try\r\n                                        {\r\n                                            Console.WriteLine($\"downloading {fileName}<{i}/{fileSum}> ...\");\r\n                                            var r = Download(folder, m.UniqueId);\r\n                                            r.Position = 0;\r\n                                            r.CopyTo(output);\r\n                                            break;\r\n                                        }\r\n                                        catch (Exception e)\r\n                                        {\r\n                                            Console.WriteLine($\"[disk Download]fail, retry, infomation:\" + e.Message);\r\n                                        }\r\n                                    }\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    Console.WriteLine($\"file {fileName}'s parts are missing, download fail\");\r\n                    return;\r\n                } \r\n            }\r\n            Console.WriteLine($\"file {fileName} download success!\");\r\n        }\r\n\r\n        /// <summary>\r\n        /// download a mail's attachments\r\n        /// </summary>\r\n        /// <param name=\"folder\">file folder</param>\r\n        /// <param name=\"id\">mail uid</param>\r\n        /// <returns>file's stream</returns>\r\n        private Stream Download(IMailFolder folder, UniqueId id)\r\n        {\r\n            Stream stream = new MemoryStream();\r\n            MimeMessage message = folder.GetMessage(id);\r\n            foreach (MimePart attachment in message.Attachments)\r\n            {\r\n                //下载附件\r\n                using (var cancel = new CancellationTokenSource())\r\n                {\r\n                    attachment.Content.DecodeTo(stream, cancel.Token);\r\n                    return stream;\r\n                }\r\n            }\r\n            return stream;\r\n        }\r\n        \r\n\r\n\r\n        /// <summary>\r\n        /// upload big files, auto split by setting's size\r\n        /// </summary>\r\n        /// <param name=\"fileName\">file name on cloud disk</param>\r\n        /// <param name=\"folderPath\">folder on email</param>\r\n        /// <param name=\"filePath\">local file path</param>\r\n        /// <param name=\"blockSize\">max size for each mail</param>\r\n        /// <returns>file upload success or not</returns>\r\n        public bool UploadBigFile(string fileName, string folderPath, string filePath, int blockSize)\r\n        {\r\n            if (!File.Exists(filePath))\r\n            {\r\n                Console.WriteLine($\"error! file {filePath} not exist!\");\r\n                return false;\r\n            }\r\n            fileName = fileName.Replace(\"\\\\\", \"/\");\r\n            while(fileName.IndexOf(\"/\") == 0)//remove the \"/\" head\r\n            {\r\n                fileName = fileName.Substring(1);\r\n            }\r\n\r\n            FileInfo fileInfo = new FileInfo(filePath);\r\n            if (fileInfo.Length > blockSize)\r\n            {\r\n\r\n                Console.WriteLine($\"[disk Upload]file need to be splited\");\r\n\r\n                var steps = (int)Math.Ceiling((double)fileInfo.Length / blockSize);\r\n                using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))\r\n                {\r\n                    using (BinaryReader br = new BinaryReader(fs))\r\n                    {\r\n                        int couter = 1;\r\n                        bool isReadingComplete = false;\r\n                        while (!isReadingComplete)\r\n                        {\r\n                            string tempName = couter.ToString();\r\n\r\n                            byte[] bytes = br.ReadBytes(blockSize);\r\n                            Stream stream = new MemoryStream(bytes);\r\n\r\n                            bool result = false;\r\n                            while(!result)\r\n                            {\r\n                                try\r\n                                {\r\n                                    result = Upload(fileName + $\"<{couter}/{steps}>\", folderPath, stream);\r\n                                }\r\n                                catch(Exception e)\r\n                                {\r\n                                    Console.WriteLine($\"[disk Upload]fail, retry, infomation:\" + e.Message);\r\n                                }\r\n                            }\r\n\r\n                            isReadingComplete = (bytes.Length != blockSize);\r\n                            if (!isReadingComplete)\r\n                            {\r\n                                couter += 1;\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                return true;\r\n            }\r\n            else\r\n            {\r\n                bool result = false;\r\n                while (!result)\r\n                {\r\n                    try\r\n                    {\r\n                        result = Upload(fileName, folderPath, File.OpenRead(filePath));\r\n                    }\r\n                    catch (Exception e)\r\n                    {\r\n                        Console.WriteLine($\"[disk Upload]fail, retry, infomation:\" + e.Message);\r\n                    }\r\n                }\r\n                return true;\r\n            }\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// get all folders in this mail\r\n        /// </summary>\r\n        /// <param name=\"path\">path, default is empty</param>\r\n        /// <returns>folder list</returns>\r\n        public IMailFolder[] GetFolders(string path = \"\")\r\n        {\r\n            ArrayList folders = new ArrayList();\r\n\r\n            var personal = GetImapClient().GetFolder(path);\r\n\r\n            foreach (var folder in personal.GetSubfolders(false))\r\n            {\r\n                if (folder.GetSubfolders(false).Count > 0)\r\n                {\r\n                    folders.Add(folder);\r\n                    folders.AddRange(GetFolders(folder.FullName));\r\n                }\r\n                else\r\n                {\r\n                    folders.Add(folder);\r\n                }\r\n            }\r\n            return (IMailFolder[])folders.ToArray(typeof(IMailFolder));\r\n        }\r\n\r\n\r\n        /// <summary>\r\n        /// create a folder\r\n        /// </summary>\r\n        /// <param name=\"path\">folder name and path</param>\r\n        /// <returns>result</returns>\r\n        public void CreatFolder(string path)\r\n        {\r\n            var client = GetImapClient();\r\n            if(path.IndexOf(\"/\") < 0)\r\n            {\r\n                var root = client.GetFolder(\"\");\r\n                root.Create(path, true);\r\n            }\r\n            else\r\n            {\r\n                int l = path.LastIndexOf(\"/\");\r\n                var folder = client.GetFolder(path.Substring(0,l));\r\n                folder.Create(path.Substring(l+1), true);\r\n            }\r\n            Console.WriteLine($\"[disk folder]folder {path} is created.\");\r\n        }\r\n\r\n        private ArrayList tempFiles = new ArrayList();\r\n        public void RefreshFiles(string folderPath)\r\n        {\r\n            tempFiles.AddRange(GetFileList(folderPath));\r\n            foreach (var s in tempFiles)\r\n            {\r\n                Console.WriteLine(s);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// upload a folder\r\n        /// </summary>\r\n        /// <param name=\"cloudPath\">cloud folder path</param>\r\n        /// <param name=\"folderPath\">mail folder</param>\r\n        /// <param name=\"localPath\">local folder path</param>\r\n        /// <param name=\"blockSize\">each mail size</param>\r\n        public void UploadFolder(string cloudPath, string folderPath, string localPath, int blockSize)\r\n        {\r\n            while (cloudPath.LastIndexOf(\"/\") == cloudPath.Length - 1)//remove last \"/\"\r\n            {\r\n                cloudPath = cloudPath.Substring(0, cloudPath.Length - 1);\r\n            }\r\n            Console.WriteLine($\"[disk upload folder]upload {localPath} to {cloudPath}\");\r\n            if (!Directory.Exists(localPath))\r\n            {\r\n                Console.WriteLine($\"error! folder {localPath} not exist!\");\r\n                return;\r\n            }\r\n\r\n            foreach (var f in Directory.GetDirectories(localPath))\r\n            {\r\n                int l = f.LastIndexOf(\"/\");\r\n                if(l == -1)\r\n                    l = f.LastIndexOf(\"\\\\\");\r\n                UploadFolder(cloudPath + f.Substring(l), folderPath, f, blockSize);\r\n            }\r\n            foreach(var f in Directory.GetFiles(localPath))\r\n            {\r\n                int l = f.LastIndexOf(\"/\");\r\n                if (l == -1)\r\n                    l = f.LastIndexOf(\"\\\\\");\r\n\r\n                string fileName = cloudPath + f.Substring(l);\r\n                fileName = fileName.Replace(\"\\\\\", \"/\");\r\n                while (fileName.IndexOf(\"/\") == 0)//remove the \"/\" head\r\n                {\r\n                    fileName = fileName.Substring(1);\r\n                }\r\n\r\n                if (tempFiles.Contains(fileName))\r\n                    Console.WriteLine($\"[disk upload folder] file {fileName} is\" +\r\n                        $\" already exist in mail disk, skip upload.\");\r\n                else\r\n                    UploadBigFile(fileName, folderPath, f, blockSize);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// download a floder\r\n        /// </summary>\r\n        /// <param name=\"cloudPath\">cloud folder path</param>\r\n        /// <param name=\"folderPath\">mail folder</param>\r\n        /// <param name=\"localPath\">local folder path</param>\r\n        public void DownloadFolder(string cloudPath, string folderPath, string localPath)\r\n        {\r\n            while (cloudPath.LastIndexOf(\"/\") == cloudPath.Length - 1)//remove last \"/\"\r\n            {\r\n                cloudPath = cloudPath.Substring(0, cloudPath.Length - 1);\r\n            }\r\n            cloudPath += \"/\";\r\n            localPath = localPath.Replace(\"\\\\\", \"/\");\r\n            if (localPath.LastIndexOf(\"/\") != localPath.Length - 1)\r\n                localPath += \"/\";\r\n            Console.WriteLine($\"[disk download folder]download {cloudPath} to {localPath}\");\r\n\r\n            var client = GetImapClient();\r\n            var folder = client.GetFolder(folderPath);\r\n            folder.Open(FolderAccess.ReadOnly);\r\n\r\n            Console.WriteLine($\"find {folder.Count} files in this folder\");\r\n            Console.WriteLine($\"fatching mails\");\r\n            var all = folder.Fetch(0,-1, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);\r\n\r\n            foreach (string f in GetFileList(folderPath))\r\n            {\r\n                if(f.IndexOf(cloudPath) == 0)//match folder\r\n                {\r\n                    string localFile = localPath + f.Substring(cloudPath.Length);\r\n                    localPath = localPath.Replace(\"\\\\\", \"/\");\r\n                    int l = localFile.LastIndexOf(\"/\");\r\n                    Directory.CreateDirectory(localFile.Substring(0, l));\r\n                    DownloadFile(folderPath, f, localFile, client, all, folder);\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "maildisk/maildisk/maildisk.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <OutputType>Exe</OutputType>\r\n    <TargetFramework>net5.0</TargetFramework>\r\n    <Authors>chenxuuu</Authors>\r\n    <Company>chenxublog.com</Company>\r\n    <Description>make your e-mail become a netdisk!</Description>\r\n    <Version>1.0.2.3</Version>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"MailKit\" Version=\"2.10.0\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.1\" />\r\n    <PackageReference Include=\"Spectre.Console\" Version=\"0.31.0\" />\r\n  </ItemGroup>\r\n</Project>\r\n"
  },
  {
    "path": "maildisk/maildisk.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.28307.168\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"maildisk\", \"maildisk\\maildisk.csproj\", \"{40D245C7-6840-4A9B-AEA9-4F9503013CC0}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {6FBAA721-B24C-48A4-AA71-CA4D6CD539C2}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  }
]