├── .github
├── FUNDING.yml
└── workflows
│ ├── build.yml
│ └── buildGUI.yml
├── .gitignore
├── LICENSE
├── MailDisk-GUI
├── MailDisk-GUI.sln
└── MailDisk-GUI
│ ├── App.config
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── FodyWeavers.xml
│ ├── FodyWeavers.xsd
│ ├── MailDisk-GUI.csproj
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
│ ├── languages
│ ├── en-US.xaml
│ └── zh-CN.xaml
│ ├── mailico.ico
│ └── packages.config
├── README.md
├── maildisk
├── maildisk.sln
└── maildisk
│ ├── Program.cs
│ ├── apis
│ ├── MailClient.cs
│ ├── Settings.cs
│ └── VisualDisk.cs
│ └── maildisk.csproj
└── pictures
├── big.psd
├── mail.psd
└── mailico.psd
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: chenxuuu
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | paths:
6 | - 'maildisk/**'
7 | - '.github/workflows/build.yml'
8 | pull_request:
9 | paths:
10 | - 'maildisk/**'
11 | workflow_dispatch:
12 | inputs:
13 | logLevel:
14 | description: 'Log level'
15 | required: true
16 | default: 'warning'
17 |
18 | jobs:
19 | build:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v1
23 | - name: environment prepare
24 | run: |
25 | sudo apt-get update
26 | sudo apt-get install -y apt-transport-https
27 | sudo apt-get update
28 | sudo apt-get install -y dotnet-sdk-5.0
29 | sudo apt-get install -y p7zip-full
30 | - name: build
31 | run: |
32 | cd maildisk
33 | dotnet publish -r win-x86 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
34 | dotnet publish -r win-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
35 | dotnet publish -r win-arm -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
36 | dotnet publish -r win-arm64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
37 | dotnet publish -r linux-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
38 | dotnet publish -r linux-arm -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
39 | dotnet publish -r linux-arm64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
40 | dotnet publish -r osx-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true --self-contained true
41 | - name: create packages
42 | run: |
43 | mkdir maildisk-pkg
44 | mv maildisk/maildisk/bin/Debug/net5.0/win-x86/publish/* maildisk-pkg/
45 | 7z a win-x86.7z maildisk-pkg/*
46 | rm maildisk-pkg/*
47 | mv maildisk/maildisk/bin/Debug/net5.0/win-x64/publish/* maildisk-pkg/
48 | 7z a win-x64.7z maildisk-pkg/*
49 | rm maildisk-pkg/*
50 | mv maildisk/maildisk/bin/Debug/net5.0/win-arm/publish/* maildisk-pkg/
51 | 7z a win-arm.7z maildisk-pkg/*
52 | rm maildisk-pkg/*
53 | mv maildisk/maildisk/bin/Debug/net5.0/win-arm64/publish/* maildisk-pkg/
54 | 7z a win-arm64.7z maildisk-pkg/*
55 | rm maildisk-pkg/*
56 | mv maildisk/maildisk/bin/Debug/net5.0/linux-x64/publish/* maildisk-pkg/
57 | 7z a linux-x64.7z maildisk-pkg/*
58 | rm maildisk-pkg/*
59 | mv maildisk/maildisk/bin/Debug/net5.0/linux-arm/publish/* maildisk-pkg/
60 | 7z a linux-arm.7z maildisk-pkg/*
61 | rm maildisk-pkg/*
62 | mv maildisk/maildisk/bin/Debug/net5.0/linux-arm64/publish/* maildisk-pkg/
63 | 7z a linux-arm64.7z maildisk-pkg/*
64 | rm maildisk-pkg/*
65 | mv maildisk/maildisk/bin/Debug/net5.0/osx-x64/publish/* maildisk-pkg/
66 | 7z a osx-x64.7z maildisk-pkg/*
67 | - uses: actions/upload-artifact@v2
68 | with:
69 | name: artifact
70 | path: |
71 | win-x86.7z
72 | win-x64.7z
73 | win-arm.7z
74 | win-arm64.7z
75 | linux-x64.7z
76 | linux-arm.7z
77 | linux-arm64.7z
78 | osx-x64.7z
79 |
--------------------------------------------------------------------------------
/.github/workflows/buildGUI.yml:
--------------------------------------------------------------------------------
1 | name: Build GUI client
2 |
3 | on:
4 | push:
5 | paths:
6 | - 'MailDisk-GUI/**'
7 | - '.github/workflows/buildGUI.yml'
8 | pull_request:
9 | paths:
10 | - 'MailDisk-GUI/**'
11 | workflow_dispatch:
12 | inputs:
13 | logLevel:
14 | description: 'Log level'
15 | required: true
16 | default: 'warning'
17 |
18 | jobs:
19 | build:
20 | runs-on: windows-latest
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v2
24 | - name: Add msbuild to PATH
25 | uses: microsoft/setup-msbuild@v1.0.2
26 | - name: Build
27 | run: |
28 | cd MailDisk-GUI
29 | nuget restore
30 | msbuild MailDisk-GUI.sln /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile
31 | - name: Upload Artifact
32 | uses: actions/upload-artifact@v2
33 | with:
34 | name: MailDisk-GUI
35 | path: MailDisk-GUI/MailDisk-GUI/bin/Release
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/visualstudio
2 |
3 | ### VisualStudio ###
4 | ## Ignore Visual Studio temporary files, build results, and
5 | ## files generated by popular Visual Studio add-ons.
6 | ##
7 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
8 |
9 | # User-specific files
10 | *.suo
11 | *.user
12 | *.userosscache
13 | *.sln.docstates
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Build results
19 | [Dd]ebug/
20 | [Dd]ebugPublic/
21 | [Rr]elease/
22 | [Rr]eleases/
23 | x64/
24 | x86/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # .NET Core
49 | project.lock.json
50 | project.fragment.lock.json
51 | artifacts/
52 | **/Properties/launchSettings.json
53 |
54 | *_i.c
55 | *_p.c
56 | *_i.h
57 | *.ilk
58 | *.meta
59 | *.obj
60 | *.pch
61 | *.pdb
62 | *.pgc
63 | *.pgd
64 | *.rsp
65 | *.sbr
66 | *.tlb
67 | *.tli
68 | *.tlh
69 | *.tmp
70 | *.tmp_proj
71 | *.log
72 | *.vspscc
73 | *.vssscc
74 | .builds
75 | *.pidb
76 | *.svclog
77 | *.scc
78 |
79 | # Chutzpah Test files
80 | _Chutzpah*
81 |
82 | # Visual C++ cache files
83 | ipch/
84 | *.aps
85 | *.ncb
86 | *.opendb
87 | *.opensdf
88 | *.sdf
89 | *.cachefile
90 | *.VC.db
91 | *.VC.VC.opendb
92 |
93 | # Visual Studio profiler
94 | *.psess
95 | *.vsp
96 | *.vspx
97 | *.sap
98 |
99 | # TFS 2012 Local Workspace
100 | $tf/
101 |
102 | # Guidance Automation Toolkit
103 | *.gpState
104 |
105 | # ReSharper is a .NET coding add-in
106 | _ReSharper*/
107 | *.[Rr]e[Ss]harper
108 | *.DotSettings.user
109 |
110 | # JustCode is a .NET coding add-in
111 | .JustCode
112 |
113 | # TeamCity is a build add-in
114 | _TeamCity*
115 |
116 | # DotCover is a Code Coverage Tool
117 | *.dotCover
118 |
119 | # Visual Studio code coverage results
120 | *.coverage
121 | *.coveragexml
122 |
123 | # NCrunch
124 | _NCrunch_*
125 | .*crunch*.local.xml
126 | nCrunchTemp_*
127 |
128 | # MightyMoose
129 | *.mm.*
130 | AutoTest.Net/
131 |
132 | # Web workbench (sass)
133 | .sass-cache/
134 |
135 | # Installshield output folder
136 | [Ee]xpress/
137 |
138 | # DocProject is a documentation generator add-in
139 | DocProject/buildhelp/
140 | DocProject/Help/*.HxT
141 | DocProject/Help/*.HxC
142 | DocProject/Help/*.hhc
143 | DocProject/Help/*.hhk
144 | DocProject/Help/*.hhp
145 | DocProject/Help/Html2
146 | DocProject/Help/html
147 |
148 | # Click-Once directory
149 | publish/
150 |
151 | # Publish Web Output
152 | *.[Pp]ublish.xml
153 | *.azurePubxml
154 | # TODO: Comment the next line if you want to checkin your web deploy settings
155 | # but database connection strings (with potential passwords) will be unencrypted
156 | *.pubxml
157 | *.publishproj
158 |
159 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
160 | # checkin your Azure Web App publish settings, but sensitive information contained
161 | # in these scripts will be unencrypted
162 | PublishScripts/
163 |
164 | # NuGet Packages
165 | *.nupkg
166 | # The packages folder can be ignored because of Package Restore
167 | **/packages/*
168 | # except build/, which is used as an MSBuild target.
169 | !**/packages/build/
170 | # Uncomment if necessary however generally it will be regenerated when needed
171 | #!**/packages/repositories.config
172 | # NuGet v3's project.json files produces more ignorable files
173 | *.nuget.props
174 | *.nuget.targets
175 |
176 | # Microsoft Azure Build Output
177 | csx/
178 | *.build.csdef
179 |
180 | # Microsoft Azure Emulator
181 | ecf/
182 | rcf/
183 |
184 | # Windows Store app package directories and files
185 | AppPackages/
186 | BundleArtifacts/
187 | Package.StoreAssociation.xml
188 | _pkginfo.txt
189 |
190 | # Visual Studio cache files
191 | # files ending in .cache can be ignored
192 | *.[Cc]ache
193 | # but keep track of directories ending in .cache
194 | !*.[Cc]ache/
195 |
196 | # Others
197 | ClientBin/
198 | ~$*
199 | *~
200 | *.dbmdl
201 | *.dbproj.schemaview
202 | *.jfm
203 | *.pfx
204 | *.publishsettings
205 | orleans.codegen.cs
206 |
207 | # Since there are multiple workflows, uncomment next line to ignore bower_components
208 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
209 | #bower_components/
210 |
211 | # RIA/Silverlight projects
212 | Generated_Code/
213 |
214 | # Backup & report files from converting an old project file
215 | # to a newer Visual Studio version. Backup files are not needed,
216 | # because we have git ;-)
217 | _UpgradeReport_Files/
218 | Backup*/
219 | UpgradeLog*.XML
220 | UpgradeLog*.htm
221 |
222 | # SQL Server files
223 | *.mdf
224 | *.ldf
225 |
226 | # Business Intelligence projects
227 | *.rdl.data
228 | *.bim.layout
229 | *.bim_*.settings
230 |
231 | # Microsoft Fakes
232 | FakesAssemblies/
233 |
234 | # GhostDoc plugin setting file
235 | *.GhostDoc.xml
236 |
237 | # Node.js Tools for Visual Studio
238 | .ntvs_analysis.dat
239 | node_modules/
240 |
241 | # Typescript v1 declaration files
242 | typings/
243 |
244 | # Visual Studio 6 build log
245 | *.plg
246 |
247 | # Visual Studio 6 workspace options file
248 | *.opt
249 |
250 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
251 | *.vbw
252 |
253 | # Visual Studio LightSwitch build output
254 | **/*.HTMLClient/GeneratedArtifacts
255 | **/*.DesktopClient/GeneratedArtifacts
256 | **/*.DesktopClient/ModelManifest.xml
257 | **/*.Server/GeneratedArtifacts
258 | **/*.Server/ModelManifest.xml
259 | _Pvt_Extensions
260 |
261 | # Paket dependency manager
262 | .paket/paket.exe
263 | paket-files/
264 |
265 | # FAKE - F# Make
266 | .fake/
267 |
268 | # JetBrains Rider
269 | .idea/
270 | *.sln.iml
271 |
272 | # CodeRush
273 | .cr/
274 |
275 | # Python Tools for Visual Studio (PTVS)
276 | __pycache__/
277 | *.pyc
278 |
279 | # Cake - Uncomment if you are using it
280 | # tools/**
281 | # !tools/packages.config
282 |
283 | # End of https://www.gitignore.io/api/visualstudio
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and
10 | distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
13 | owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities
16 | that control, are controlled by, or are under common control with that entity.
17 | For the purposes of this definition, "control" means (i) the power, direct or
18 | indirect, to cause the direction or management of such entity, whether by
19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 | outstanding shares, or (iii) beneficial ownership of such entity.
21 |
22 | "You" (or "Your") shall mean an individual or Legal Entity exercising
23 | permissions granted by this License.
24 |
25 | "Source" form shall mean the preferred form for making modifications, including
26 | but not limited to software source code, documentation source, and configuration
27 | files.
28 |
29 | "Object" form shall mean any form resulting from mechanical transformation or
30 | translation of a Source form, including but not limited to compiled object code,
31 | generated documentation, and conversions to other media types.
32 |
33 | "Work" shall mean the work of authorship, whether in Source or Object form, made
34 | available under the License, as indicated by a copyright notice that is included
35 | in or attached to the work (an example is provided in the Appendix below).
36 |
37 | "Derivative Works" shall mean any work, whether in Source or Object form, that
38 | is based on (or derived from) the Work and for which the editorial revisions,
39 | annotations, elaborations, or other modifications represent, as a whole, an
40 | original work of authorship. For the purposes of this License, Derivative Works
41 | shall not include works that remain separable from, or merely link (or bind by
42 | name) to the interfaces of, the Work and Derivative Works thereof.
43 |
44 | "Contribution" shall mean any work of authorship, including the original version
45 | of the Work and any modifications or additions to that Work or Derivative Works
46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
47 | by the copyright owner or by an individual or Legal Entity authorized to submit
48 | on behalf of the copyright owner. For the purposes of this definition,
49 | "submitted" means any form of electronic, verbal, or written communication sent
50 | to the Licensor or its representatives, including but not limited to
51 | communication on electronic mailing lists, source code control systems, and
52 | issue tracking systems that are managed by, or on behalf of, the Licensor for
53 | the purpose of discussing and improving the Work, but excluding communication
54 | that is conspicuously marked or otherwise designated in writing by the copyright
55 | owner as "Not a Contribution."
56 |
57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58 | of whom a Contribution has been received by Licensor and subsequently
59 | incorporated within the Work.
60 |
61 | 2. Grant of Copyright License.
62 |
63 | Subject to the terms and conditions of this License, each Contributor hereby
64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65 | irrevocable copyright license to reproduce, prepare Derivative Works of,
66 | publicly display, publicly perform, sublicense, and distribute the Work and such
67 | Derivative Works in Source or Object form.
68 |
69 | 3. Grant of Patent License.
70 |
71 | Subject to the terms and conditions of this License, each Contributor hereby
72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73 | irrevocable (except as stated in this section) patent license to make, have
74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75 | such license applies only to those patent claims licensable by such Contributor
76 | that are necessarily infringed by their Contribution(s) alone or by combination
77 | of their Contribution(s) with the Work to which such Contribution(s) was
78 | submitted. If You institute patent litigation against any entity (including a
79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80 | Contribution incorporated within the Work constitutes direct or contributory
81 | patent infringement, then any patent licenses granted to You under this License
82 | for that Work shall terminate as of the date such litigation is filed.
83 |
84 | 4. Redistribution.
85 |
86 | You may reproduce and distribute copies of the Work or Derivative Works thereof
87 | in any medium, with or without modifications, and in Source or Object form,
88 | provided that You meet the following conditions:
89 |
90 | You must give any other recipients of the Work or Derivative Works a copy of
91 | this License; and
92 | You must cause any modified files to carry prominent notices stating that You
93 | changed the files; and
94 | You must retain, in the Source form of any Derivative Works that You distribute,
95 | all copyright, patent, trademark, and attribution notices from the Source form
96 | of the Work, excluding those notices that do not pertain to any part of the
97 | Derivative Works; and
98 | If the Work includes a "NOTICE" text file as part of its distribution, then any
99 | Derivative Works that You distribute must include a readable copy of the
100 | attribution notices contained within such NOTICE file, excluding those notices
101 | that do not pertain to any part of the Derivative Works, in at least one of the
102 | following places: within a NOTICE text file distributed as part of the
103 | Derivative Works; within the Source form or documentation, if provided along
104 | with the Derivative Works; or, within a display generated by the Derivative
105 | Works, if and wherever such third-party notices normally appear. The contents of
106 | the NOTICE file are for informational purposes only and do not modify the
107 | License. You may add Your own attribution notices within Derivative Works that
108 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
109 | provided that such additional attribution notices cannot be construed as
110 | modifying the License.
111 | You may add Your own copyright statement to Your modifications and may provide
112 | additional or different license terms and conditions for use, reproduction, or
113 | distribution of Your modifications, or for any such Derivative Works as a whole,
114 | provided Your use, reproduction, and distribution of the Work otherwise complies
115 | with the conditions stated in this License.
116 |
117 | 5. Submission of Contributions.
118 |
119 | Unless You explicitly state otherwise, any Contribution intentionally submitted
120 | for inclusion in the Work by You to the Licensor shall be under the terms and
121 | conditions of this License, without any additional terms or conditions.
122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
123 | any separate license agreement you may have executed with Licensor regarding
124 | such Contributions.
125 |
126 | 6. Trademarks.
127 |
128 | This License does not grant permission to use the trade names, trademarks,
129 | service marks, or product names of the Licensor, except as required for
130 | reasonable and customary use in describing the origin of the Work and
131 | reproducing the content of the NOTICE file.
132 |
133 | 7. Disclaimer of Warranty.
134 |
135 | Unless required by applicable law or agreed to in writing, Licensor provides the
136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138 | including, without limitation, any warranties or conditions of TITLE,
139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140 | solely responsible for determining the appropriateness of using or
141 | redistributing the Work and assume any risks associated with Your exercise of
142 | permissions under this License.
143 |
144 | 8. Limitation of Liability.
145 |
146 | In no event and under no legal theory, whether in tort (including negligence),
147 | contract, or otherwise, unless required by applicable law (such as deliberate
148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
149 | liable to You for damages, including any direct, indirect, special, incidental,
150 | or consequential damages of any character arising as a result of this License or
151 | out of the use or inability to use the Work (including but not limited to
152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153 | any and all other commercial damages or losses), even if such Contributor has
154 | been advised of the possibility of such damages.
155 |
156 | 9. Accepting Warranty or Additional Liability.
157 |
158 | While redistributing the Work or Derivative Works thereof, You may choose to
159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160 | other liability obligations and/or rights consistent with this License. However,
161 | in accepting such obligations, You may act only on Your own behalf and on Your
162 | sole responsibility, not on behalf of any other Contributor, and only if You
163 | agree to indemnify, defend, and hold each Contributor harmless for any liability
164 | incurred by, or claims asserted against, such Contributor by reason of your
165 | accepting any such warranty or additional liability.
166 |
167 | END OF TERMS AND CONDITIONS
168 |
169 | APPENDIX: How to apply the Apache License to your work
170 |
171 | To apply the Apache License to your work, attach the following boilerplate
172 | notice, with the fields enclosed by brackets "{}" replaced with your own
173 | identifying information. (Don't include the brackets!) The text should be
174 | enclosed in the appropriate comment syntax for the file format. We also
175 | recommend that a file or class name and description of purpose be included on
176 | the same "printed page" as the copyright notice for easier identification within
177 | third-party archives.
178 |
179 | Copyright 2017 晨旭
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | http://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
193 |
194 | The MIT License
195 |
196 | Copyright (c) 2012-2014 Torben Könke
197 |
198 | Permission 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:
199 |
200 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
201 |
202 | THE 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.
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30717.126
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MailDisk-GUI", "MailDisk-GUI\MailDisk-GUI.csproj", "{B21FA876-FD48-42F0-8B2B-A08B24671DB9}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {B21FA876-FD48-42F0-8B2B-A08B24671DB9}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {99ABBE37-373C-4151-9CC1-8F0A99D7C420}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace MailDisk_GUI
10 | {
11 | ///
12 | /// App.xaml 的交互逻辑
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Used to control if the On_PropertyName_Changed feature is enabled.
12 |
13 |
14 |
15 |
16 | Used to control if the Dependent properties feature is enabled.
17 |
18 |
19 |
20 |
21 | Used to control if the IsChanged property feature is enabled.
22 |
23 |
24 |
25 |
26 | 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.
27 |
28 |
29 |
30 |
31 | Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.
32 |
33 |
34 |
35 |
36 | Used to control if equality checks should use the Equals method resolved from the base class.
37 |
38 |
39 |
40 |
41 | Used to control if equality checks should use the static Equals method resolved from the base class.
42 |
43 |
44 |
45 |
46 | Used to turn off build warnings from this weaver.
47 |
48 |
49 |
50 |
51 | Used to turn off build warnings about mismatched On_PropertyName_Changed methods.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
62 |
63 |
64 |
65 |
66 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
67 |
68 |
69 |
70 |
71 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
72 |
73 |
74 |
75 |
76 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
77 |
78 |
79 |
80 |
81 | The order of preloaded assemblies, delimited with line breaks.
82 |
83 |
84 |
85 |
86 |
87 | 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.
88 |
89 |
90 |
91 |
92 | Controls if .pdbs for reference assemblies are also embedded.
93 |
94 |
95 |
96 |
97 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
98 |
99 |
100 |
101 |
102 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
103 |
104 |
105 |
106 |
107 | 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.
108 |
109 |
110 |
111 |
112 | 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.
113 |
114 |
115 |
116 |
117 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
118 |
119 |
120 |
121 |
122 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
123 |
124 |
125 |
126 |
127 | A list of unmanaged 32 bit assembly names to include, delimited with |.
128 |
129 |
130 |
131 |
132 | A list of unmanaged 64 bit assembly names to include, delimited with |.
133 |
134 |
135 |
136 |
137 | The order of preloaded assemblies, delimited with |.
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
146 |
147 |
148 |
149 |
150 | A comma-separated list of error codes that can be safely ignored in assembly verification.
151 |
152 |
153 |
154 |
155 | 'false' to turn off automatic generation of the XML Schema file.
156 |
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/MailDisk-GUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Debug
8 | AnyCPU
9 | {B21FA876-FD48-42F0-8B2B-A08B24671DB9}
10 | WinExe
11 | MailDisk_GUI
12 | MailDisk-GUI
13 | v4.6.2
14 | 512
15 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | 4
17 | true
18 | true
19 |
20 |
21 |
22 |
23 | AnyCPU
24 | true
25 | full
26 | false
27 | bin\Debug\
28 | DEBUG;TRACE
29 | prompt
30 | 4
31 |
32 |
33 | AnyCPU
34 | pdbonly
35 | true
36 | bin\Release\
37 | TRACE
38 | prompt
39 | 4
40 |
41 |
42 | mailico.ico
43 |
44 |
45 |
46 | ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll
47 |
48 |
49 | ..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll
50 |
51 |
52 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
53 |
54 |
55 | ..\packages\PropertyChanged.Fody.3.3.1\lib\net40\PropertyChanged.dll
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | 4.0
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | MSBuild:Compile
75 | Designer
76 |
77 |
78 | MSBuild:Compile
79 | Designer
80 |
81 |
82 | Designer
83 | MSBuild:Compile
84 |
85 |
86 | MSBuild:Compile
87 | Designer
88 |
89 |
90 | App.xaml
91 | Code
92 |
93 |
94 | MainWindow.xaml
95 | Code
96 |
97 |
98 |
99 |
100 | Code
101 |
102 |
103 | True
104 | True
105 | Resources.resx
106 |
107 |
108 | True
109 | Settings.settings
110 | True
111 |
112 |
113 | ResXFileCodeGenerator
114 | Resources.Designer.cs
115 |
116 |
117 |
118 | SettingsSingleFileGenerator
119 | Settings.Designer.cs
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | 9.0
141 |
142 |
143 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Navigation;
14 | using System.Windows.Shapes;
15 |
16 | namespace MailDisk_GUI
17 | {
18 | ///
19 | /// MainWindow.xaml 的交互逻辑
20 | ///
21 | public partial class MainWindow : Window
22 | {
23 | public MainWindow()
24 | {
25 | InitializeComponent();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // 有关程序集的一般信息由以下
8 | // 控制。更改这些特性值可修改
9 | // 与程序集关联的信息。
10 | [assembly: AssemblyTitle("MailDisk-GUI")]
11 | [assembly: AssemblyDescription("邮箱网盘")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("chenxublog.com")]
14 | [assembly: AssemblyProduct("MailDisk-GUI")]
15 | [assembly: AssemblyCopyright("Copyright © chenxuuu")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // 将 ComVisible 设置为 false 会使此程序集中的类型
20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
21 | //请将此类型的 ComVisible 特性设置为 true。
22 | [assembly: ComVisible(false)]
23 |
24 | //若要开始生成可本地化的应用程序,请设置
25 | //.csproj 文件中的 CultureYouAreCodingWith
26 | //例如,如果您在源文件中使用的是美国英语,
27 | //使用的是美国英语,请将 设置为 en-US。 然后取消
28 | //对以下 NeutralResourceLanguage 特性的注释。 更新
29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置
36 | //(未在页面中找到资源时使用,
37 | //或应用程序资源字典中找到时使用)
38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
39 | //(未在页面中找到资源时使用,
40 | //、应用程序或任何主题专用资源字典中找到时使用)
41 | )]
42 |
43 |
44 | // 程序集的版本信息由下列四个值组成:
45 | //
46 | // 主版本
47 | // 次版本
48 | // 生成号
49 | // 修订号
50 | //
51 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
52 | //通过使用 "*",如下所示:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本: 4.0.30319.42000
5 | //
6 | // 对此文件的更改可能导致不正确的行为,如果
7 | // 重新生成代码,则所做更改将丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 |
12 | namespace MailDisk_GUI.Properties
13 | {
14 | ///
15 | /// 强类型资源类,用于查找本地化字符串等。
16 | ///
17 | // 此类是由 StronglyTypedResourceBuilder
18 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
19 | // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
20 | // (以 /str 作为命令选项),或重新生成 VS 项目。
21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
24 | internal class Resources
25 | {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources()
33 | {
34 | }
35 |
36 | ///
37 | /// 返回此类使用的缓存 ResourceManager 实例。
38 | ///
39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
40 | internal static global::System.Resources.ResourceManager ResourceManager
41 | {
42 | get
43 | {
44 | if ((resourceMan == null))
45 | {
46 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MailDisk_GUI.Properties.Resources", typeof(Resources).Assembly);
47 | resourceMan = temp;
48 | }
49 | return resourceMan;
50 | }
51 | }
52 |
53 | ///
54 | /// 重写当前线程的 CurrentUICulture 属性,对
55 | /// 使用此强类型资源类的所有资源查找执行重写。
56 | ///
57 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
58 | internal static global::System.Globalization.CultureInfo Culture
59 | {
60 | get
61 | {
62 | return resourceCulture;
63 | }
64 | set
65 | {
66 | resourceCulture = value;
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 |
12 | namespace MailDisk_GUI.Properties
13 | {
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
17 | {
18 |
19 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
20 |
21 | public static Settings Default
22 | {
23 | get
24 | {
25 | return defaultInstance;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/languages/en-US.xaml:
--------------------------------------------------------------------------------
1 |
5 | MailDisk
6 | Files
7 | Jobs
8 | Settings
9 | About
10 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/languages/zh-CN.xaml:
--------------------------------------------------------------------------------
1 |
5 | 邮箱网盘
6 | 文件
7 | 任务
8 | 设置
9 | 关于
10 |
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/mailico.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenxuuu/Mail-Box-Net-Disk/dcb1222d01c5596fd6073898f37650a31f35292b/MailDisk-GUI/MailDisk-GUI/mailico.ico
--------------------------------------------------------------------------------
/MailDisk-GUI/MailDisk-GUI/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 邮箱网盘
2 |
3 | 
4 | 
5 |
6 | 利用邮箱实现网盘功能的工具。
7 |
8 | 本项目有两个仓库,不过建议在GitHub进行star/pr操作:
9 |
10 | GitHub:[https://github.com/chenxuuu/Mail-Box-Net-Disk](https://github.com/chenxuuu/Mail-Box-Net-Disk)
11 |
12 | git.osc:[https://gitee.com/chenxuuu/Mail-Box-Net-Disk](https://gitee.com/chenxuuu/Mail-Box-Net-Disk)
13 |
14 | ## 下载
15 |
16 | 每次发布版本,会自动在GitHub的release页更新:[点击前往](https://github.com/chenxuuu/Mail-Box-Net-Disk/releases/latest)
17 |
18 | ## 原理
19 |
20 | 利用邮箱附件作为文件存储空间,实现文件的上传下载功能
21 |
22 | ## 功能
23 |
24 | - [x] 支持smtp/imap协议的邮箱
25 | - [x] 读取文件夹列表/新建文件夹
26 | - [x] 上传小于限制大小的文件
27 | - [x] 读取已存在的文件
28 | - [x] 上传大文件自动分卷
29 | - [x] 识别分卷上传的文件,下载自动合并
30 | - [x] 优化文件搜索速度
31 | - [x] 完成命令行工具成品
32 | - [x] 支持文件夹上传
33 | - [x] 支持文件夹下载
34 | - [ ] win系统下的gui管理工具
35 | - [ ] 其他系统下的gui管理工具
36 |
37 | ## 命令列表
38 |
39 | ```cmd
40 | maildisk -h 查看命令帮助
41 |
42 | -s
43 | 更改邮箱参数设置
44 |
45 | -lf
46 | 列出邮箱的所有邮件文件夹
47 |
48 | -cf <邮件文件夹名>
49 | 新建一个邮件文件夹
50 |
51 | -l <邮件文件夹>
52 | 列出所有该邮件文件夹下的文件
53 |
54 | -c <邮件文件夹>
55 | 清除所有该邮件文件夹下不完整的分卷文件
56 |
57 | -u <邮件文件夹> <本地文件> <云端文件>
58 | 上传文件
59 |
60 | -d <邮件文件夹> <本地文件> <云端文件>
61 | 下载文件
62 |
63 | -uf <邮件文件夹> <本地文件夹> <云端虚拟文件夹路径>
64 | 上传文件夹,并清理不完整分卷的邮件
65 | 注意:如果云端存在该文件,则不会上传
66 |
67 | -df <邮件文件夹> <本地文件夹> <云端虚拟文件夹路径>
68 | 下载文件夹
69 |
70 | 所有文件和路径都不能包含'<'符号
71 | ```
72 |
73 | ## 加入该项目
74 |
75 | 如果你愿意忍受我写的智障代码,那么欢迎pr
76 |
77 | ## 其他
78 |
79 | 该项目的代码可以随意引用,但请保留指向该项目的说明文字
80 |
--------------------------------------------------------------------------------
/maildisk/maildisk.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.168
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "maildisk", "maildisk\maildisk.csproj", "{40D245C7-6840-4A9B-AEA9-4F9503013CC0}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {40D245C7-6840-4A9B-AEA9-4F9503013CC0}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {6FBAA721-B24C-48A4-AA71-CA4D6CD539C2}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/maildisk/maildisk/Program.cs:
--------------------------------------------------------------------------------
1 | using MailKit;
2 | using MailKit.Net.Imap;
3 | using MailKit.Search;
4 | using System;
5 | using maildisk.apis;
6 | using System.IO;
7 | using System.Text;
8 |
9 | namespace maildisk
10 | {
11 | class Program
12 | {
13 | static void Main(string[] args)
14 | {
15 | if(args.Length > 0)
16 | switch(args[0])
17 | {
18 | case "-h":
19 | Console.WriteLine(@"
20 | ***********
21 | *Mail Disk*
22 | ***********
23 | You can use these commands:
24 |
25 | -h:
26 | show commands we support.
27 |
28 | -s:
29 | set your imap settings.
30 |
31 | -lf:
32 | list all folders on mail server.
33 |
34 | -cf :
35 | create new folder on mail server.
36 |
37 | -l :
38 | show files in this folder.
39 |
40 | -c :
41 | clear all wrong files in this folder.
42 |
43 | -u :
44 | upload a file to net disk.
45 |
46 | -d :
47 | download a file from net disk.
48 |
49 | -uf :
50 | upload a folder to net disk and clear all wrong files in this email folder.
51 | Notice: if file is exist on cloud, it will not be uploaded.
52 |
53 | -df :
54 | download a folder from net disk.
55 | Notice: local file must be not exist.
56 |
57 | All file and path should not contain '<'".Replace("\r\n","\r\n\t"));
58 | return;
59 |
60 | case "-s":
61 | Settings.Set();
62 | return;
63 |
64 | case "-lf":
65 | var lfdisk = Settings.GetDisk();
66 | if (lfdisk == null) return;
67 | Console.WriteLine("getting all folders ...");
68 | var all = lfdisk.GetFolders();
69 | Console.WriteLine("here's all folders:");
70 | foreach(var f in all)
71 | {
72 | Console.WriteLine(f.FullName);
73 | }
74 | return;
75 |
76 | case "-cf":
77 | var cfdisk = Settings.GetDisk();
78 | if (cfdisk == null) return;
79 | if(args.Length < 2) { Console.WriteLine("please enter a folder name");return; }
80 | Console.WriteLine($"creating folder {args[1]} ...");
81 | cfdisk.CreatFolder(args[1]);
82 | return;
83 |
84 | case "-l":
85 | var ldisk = Settings.GetDisk();
86 | if (ldisk == null) return;
87 | if (args.Length < 2) { Console.WriteLine("please enter a folder name"); return; }
88 | Console.WriteLine($"fetching file list with folder {args[1]} ...");
89 | var lfiles = ldisk.GetFileList(args[1]);
90 | Console.WriteLine($"\r\n\r\ndone! list of files:");
91 | foreach (var s in lfiles)
92 | {
93 | Console.WriteLine(s);
94 | }
95 | return;
96 |
97 | case "-c":
98 | var cdisk = Settings.GetDisk();
99 | if (cdisk == null) return;
100 | if (args.Length < 2) { Console.WriteLine("please enter a folder name"); return; }
101 | Console.WriteLine($"fetching file list with folder {args[1]} ...");
102 | cdisk.GetFileList(args[1], true);
103 | Console.WriteLine($"\r\n\r\ndone!");
104 | return;
105 |
106 | case "-u":
107 | var udisk = Settings.GetDisk();
108 | if (udisk == null) return;
109 | if (args.Length < 4) { Console.WriteLine("wrong args count"); return; }
110 | Console.WriteLine($"uploading file {args[2]} to {args[1]} as {args[3]} ...");
111 | if(args[3].IndexOf("<")>=0)
112 | {
113 | Console.WriteLine($"error! file name do not contain '<'");
114 | return;
115 | }
116 | udisk.UploadBigFile(args[3], args[1], args[2], (int)Settings.maxBlock * 1024 * 1024);
117 | return;
118 |
119 | case "-d":
120 | var ddisk = Settings.GetDisk();
121 | if (ddisk == null) return;
122 | if (args.Length < 4) { Console.WriteLine("wrong args count"); return; }
123 | Console.WriteLine($"Download file {args[3]} from {args[1]} as {args[2]} ...");
124 | if (args[3].IndexOf("<") >= 0)
125 | {
126 | Console.WriteLine($"error! file name do not contain '<'");
127 | return;
128 | }
129 | ddisk.DownloadFile(args[1], args[3], args[2]);
130 | return;
131 |
132 | case "-uf":
133 | var ufdisk = Settings.GetDisk();
134 | if (ufdisk == null) return;
135 | if (args.Length < 4) { Console.WriteLine("wrong args count"); return; }
136 | Console.WriteLine($"upload folder {args[3]} to {args[1]} as {args[2]} ...");
137 | if (args[3].IndexOf("<") >= 0)
138 | {
139 | Console.WriteLine($"error! folder name do not contain '<'");
140 | return;
141 | }
142 | ufdisk.RefreshFiles(args[1]);
143 | ufdisk.UploadFolder(args[3], args[1], args[2], (int)Settings.maxBlock * 1024 * 1024);
144 | Console.WriteLine("done! all files uploaded!");
145 | return;
146 |
147 | case "-df":
148 | var dfdisk = Settings.GetDisk();
149 | if (dfdisk == null) return;
150 | if (args.Length < 4) { Console.WriteLine("wrong args count"); return; }
151 | Console.WriteLine($"Download folder {args[3]} from {args[1]} as {args[2]} ...");
152 | if (args[3].IndexOf("<") >= 0)
153 | {
154 | Console.WriteLine($"error! folder name do not contain '<'");
155 | return;
156 | }
157 | dfdisk.DownloadFolder(args[3], args[1], args[2]);
158 | Console.WriteLine("done! all files downloaded!");
159 | return;
160 |
161 | default:
162 | break;
163 | }
164 | Console.WriteLine(@"no commond matched
165 | use -h to show commands we support");
166 | return;
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/maildisk/maildisk/apis/MailClient.cs:
--------------------------------------------------------------------------------
1 | using MailKit;
2 | using MailKit.Net.Imap;
3 | using MailKit.Search;
4 | using System;
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using System.Text;
8 |
9 | namespace maildisk.apis
10 | {
11 | class MailClient
12 | {
13 | private string address;
14 | private ImapClient client = new ImapClient();
15 |
16 | ///
17 | /// new mail client
18 | ///
19 | /// server address
20 | /// server port
21 | /// server use ssl or not
22 | /// your account
23 | /// your password
24 | /// your email address
25 | public MailClient(string server, int port, bool useSsl, string account, string password, string address)
26 | {
27 | client.ServerCertificateValidationCallback = (s, c, h, e) => true;
28 | client.Connect(server, port, useSsl);
29 | client.Authenticate(account, password);
30 | this.address = address;
31 | #if DEBUG
32 | Console.WriteLine($"[mail create]mail client created, on {server}:{port}" +
33 | $", ssl:{useSsl}, {account}, {address}");
34 | #endif
35 | }
36 |
37 | ///
38 | /// get all folders in this mail
39 | ///
40 | /// path, default is empty
41 | /// folder list
42 | public IMailFolder[] GetFolders(string path = "")
43 | {
44 | ArrayList folders = new ArrayList();
45 |
46 | var personal = client.GetFolder(path);
47 |
48 | foreach (var folder in personal.GetSubfolders(false))
49 | {
50 | if (folder.GetSubfolders(false).Count > 0)
51 | {
52 | #if DEBUG
53 | Console.WriteLine($"[folder open] {path + folder.Name}");
54 | #endif
55 | folders.AddRange(GetFolders(folder.FullName));
56 | }
57 | else
58 | {
59 | folders.Add(folder);
60 | #if DEBUG
61 | Console.WriteLine($"[folder get] {folder.FullName}");
62 | #endif
63 | }
64 | }
65 | return (IMailFolder[])folders.ToArray(typeof(IMailFolder));
66 | }
67 |
68 | ///
69 | /// get one folder
70 | ///
71 | /// path, default is empty
72 | /// folder
73 | public IMailFolder GetFolder(string path)
74 | {
75 | return client.GetFolder(path);
76 | }
77 |
78 |
79 | ///
80 | /// get not read mails, and mark as seen
81 | ///
82 | /// mails' IMessageSummary
83 | public IList GetNotSeen()
84 | {
85 | ArrayList mails = new ArrayList();
86 | var inbox = client.Inbox;
87 | inbox.Open(FolderAccess.ReadWrite);
88 | var uids = inbox.Search(SearchQuery.NotSeen);
89 | inbox.AddFlags(uids, MessageFlags.Seen, true);
90 | #if DEBUG
91 | Console.WriteLine($"[GetNotSeen] mails count {uids.Count}");
92 | #endif
93 | return inbox.Fetch(uids,
94 | MessageSummaryItems.Full | MessageSummaryItems.UniqueId);
95 | }
96 |
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/maildisk/maildisk/apis/Settings.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Text;
8 |
9 | namespace maildisk.apis
10 | {
11 | class Settings
12 | {
13 | public static long maxBlock = 0;
14 |
15 | private static string GetBasePath()
16 | {
17 | using var processModule = Process.GetCurrentProcess().MainModule;
18 | return Path.GetDirectoryName(processModule?.FileName);
19 | }
20 |
21 | ///
22 | /// check setting file exist
23 | ///
24 | /// file exist
25 | public static bool CheckSetings()
26 | {
27 | return File.Exists(GetBasePath() + "/mail.json");
28 | }
29 |
30 | ///
31 | /// create settings file
32 | ///
33 | public static void Set()
34 | {
35 | Console.WriteLine("Notice: If you're using QQMail, pleause create a login code on mail.qq.com\r\n");
36 | JObject o = new JObject();
37 | Console.Write("Enter the imap server address:");
38 | o["imap"] = Console.ReadLine();
39 |
40 | Console.Write("Enter the imap server port(default 993):");
41 | try { o["port"] = int.Parse(Console.ReadLine()); }
42 | catch { o["port"] = 993; }
43 |
44 | Console.Write("Use ssl?(y/n):");
45 | o["ssl"] = Console.ReadLine() == "y" ? true : false;
46 |
47 | Console.Write("Account:");
48 | o["account"] = Console.ReadLine();
49 |
50 | Console.Write("Password:");
51 | o["password"] = Console.ReadLine();
52 |
53 | Console.Write("E-mail address:");
54 | o["address"] = Console.ReadLine();
55 |
56 | Console.Write("Max size for each file(MiB):");
57 | o["block"] = Console.ReadLine();
58 |
59 | File.WriteAllText(GetBasePath() + "/mail.json", o.ToString());
60 |
61 | Console.WriteLine("\r\ndone! enjoy!");
62 | }
63 |
64 | ///
65 | /// return a VisualDisk
66 | ///
67 | /// VisualDisk
68 | public static VisualDisk GetDisk()
69 | {
70 | if (CheckSetings())
71 | {
72 | string s = File.ReadAllText(GetBasePath() + "/mail.json");
73 | JObject jo = (JObject)JsonConvert.DeserializeObject(s);
74 | var disk = new VisualDisk(
75 | (string)jo["imap"],
76 | (int)jo["port"],
77 | (bool)jo["ssl"],
78 | (string)jo["account"],
79 | (string)jo["password"],
80 | (string)jo["address"]);
81 | maxBlock = (long)jo["block"];
82 | return disk;
83 | }
84 | else
85 | {
86 | Console.WriteLine("settings not found\r\npleause use -s command to set imap settings");
87 | return null;
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/maildisk/maildisk/apis/VisualDisk.cs:
--------------------------------------------------------------------------------
1 | using MailKit;
2 | using MailKit.Net.Imap;
3 | using MailKit.Net.Smtp;
4 | using MailKit.Search;
5 | using MimeKit;
6 | using System;
7 | using System.Collections;
8 | using System.Collections.Generic;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Text;
12 | using System.Text.RegularExpressions;
13 | using System.Threading;
14 | using System.Threading.Tasks;
15 |
16 | namespace maildisk.apis
17 | {
18 | class VisualDisk
19 | {
20 | private string address;
21 | private string account;
22 | private string password;
23 | private string imapServer;
24 | private int imapPort;
25 | private bool imapSsl;
26 | private string lastFolder;
27 |
28 | public VisualDisk(string imapServer, int port, bool useSsl, string account, string password, string address)
29 | {
30 | this.imapServer = imapServer;
31 | this.imapPort = port;
32 | this.imapSsl = useSsl;
33 | this.address = address;
34 | this.account = account;
35 | this.password = password;
36 | //auto mark as seen task
37 | Task.Run(() => {
38 | while(true)
39 | {
40 | try
41 | {
42 | if (lastFolder != null)
43 | {
44 | var client = GetImapClient();
45 | var f = client.GetFolder(lastFolder);
46 | f.Open(FolderAccess.ReadWrite);
47 | var uids = f.Search(SearchQuery.NotSeen);
48 | foreach (var u in uids)
49 | {
50 | f.AddFlags(u, MessageFlags.Seen, true);
51 |
52 | Console.WriteLine($"[disk check]add a seen flag");
53 |
54 | }
55 | client.Disconnect(true);
56 | }
57 | }
58 | catch(Exception e)
59 | {
60 | Console.WriteLine($"[disk check]fetch error: {e.Message}");
61 | }
62 | Task.Delay(30 * 1000).Wait();
63 | }
64 | });
65 | }
66 |
67 | ///
68 | /// get a imap client
69 | ///
70 | /// imap client
71 | public ImapClient GetImapClient()
72 | {
73 | ImapClient client = new ImapClient();
74 | client.ServerCertificateValidationCallback = (s, c, h, e) => true;
75 | client.Connect(imapServer, imapPort, imapSsl);
76 | client.Authenticate(account, password);
77 | //判断是否 添加ID COMMOND命令
78 | if ((client.Capabilities | ImapCapabilities.Id) == client.Capabilities)
79 | {
80 | var clientImplementation = new ImapImplementation
81 | {
82 | Name = "Foxmail",
83 | Version = "9.156"
84 | };
85 | var serverImplementation = client.Identify(clientImplementation);
86 | }
87 | return client;
88 | }
89 |
90 | ///
91 | /// upload a file
92 | ///
93 | /// file name on cloud disk
94 | /// folder on email
95 | /// local file path
96 | /// file upload success or not
97 | private bool Upload(string fileName, string folderPath, Stream file)
98 | {
99 |
100 | Console.WriteLine($"[disk upload]upload {fileName} to mail folder {folderPath}, size:{file.Length}");
101 |
102 | var client = GetImapClient();
103 | var message = new MimeMessage();
104 | message.From.Add(MailboxAddress.Parse(address));
105 | message.To.Add(MailboxAddress.Parse(address));
106 | message.Subject = "[mailDisk]" + fileName;
107 | var body = new TextPart("plain")
108 | {
109 | Text =
110 | "This mail was send by mail disk\r\n" +
111 | "please do not delete"
112 | };
113 |
114 | var attachment = new MimePart()
115 | {
116 | Content = new MimeContent(file, ContentEncoding.Default),
117 | ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
118 | ContentTransferEncoding = ContentEncoding.Base64,
119 | FileName = "attachment.netdiskfile"
120 | };
121 |
122 | var multipart = new Multipart("mixed");
123 | multipart.Add(body);
124 | multipart.Add(attachment);
125 | message.Body = multipart;
126 |
127 | Console.WriteLine("[disk upload]appending...");
128 |
129 | var folder = GetImapClient().GetFolder(folderPath);
130 | folder.Open(FolderAccess.ReadWrite);
131 | var uid = folder.Append(message);
132 | lastFolder = folderPath;
133 |
134 |
135 | Console.WriteLine($"[disk upload]upload success");
136 |
137 | client.Disconnect(true);
138 | return true;
139 | }
140 |
141 | ///
142 | /// get file list with folder
143 | ///
144 | /// folder
145 | /// files' name
146 | public string[] GetFileList(string folderPath, bool deleteMissing = false)
147 | {
148 | ArrayList mails = new ArrayList();
149 | var client = GetImapClient();
150 | var folder = client.GetFolder(folderPath);
151 | folder.Open(FolderAccess.ReadWrite);
152 | Console.WriteLine($"find {folder.Count} mails in this folder");
153 | for(int i = 0;i < folder.Count; i += 100)
154 | {
155 | Console.WriteLine($"fatching mails {i}/{folder.Count}");
156 | int max = i + 100;
157 | if (max >= folder.Count)
158 | max = folder.Count - 1;
159 | foreach (var m in folder.Fetch(i, max, MessageSummaryItems.Full | MessageSummaryItems.UniqueId))
160 | {
161 | if(m.Envelope.Subject.IndexOf("[mailDisk]") == 0)
162 | mails.Add(m.Envelope.Subject.Substring("[mailDisk]".Length));
163 | }
164 | if (max == folder.Count)
165 | break;
166 | }
167 | Console.WriteLine($"mails in {folder.Count} fatched ok.");
168 |
169 | ArrayList files = new ArrayList();
170 | foreach(string f in mails)
171 | {
172 | if (f.IndexOf("<")>=0)
173 | {
174 | MatchCollection mc = Regex.Matches(f, @"(.+?)<1/(\d+?)>");
175 | if (mc.Count > 0 && mc[0].Groups.Count == 3)
176 | {
177 | Console.WriteLine($"find file {mc[0].Groups[1]} with {mc[0].Groups[2]} parts,checking...");
178 | bool result = true;//check is it have all files
179 | int sum = int.Parse(mc[0].Groups[2].ToString());
180 | for(int i=1;i<=sum;i++)
181 | {
182 | if(!mails.Contains($"{mc[0].Groups[1]}<{i}/{sum}>"))
183 | {
184 | result = false;
185 | break;
186 | }
187 | }
188 | if(result)
189 | {
190 | files.Add(mc[0].Groups[1].ToString());
191 | Console.WriteLine($"file {mc[0].Groups[1]} check ok");
192 | }
193 | else
194 | Console.WriteLine($"file {mc[0].Groups[1]}'s parts are missing");
195 | }
196 |
197 | }
198 | else
199 | {
200 | files.Add(f);
201 | }
202 | }
203 | if(deleteMissing)
204 | {
205 | Console.WriteLine("start cleaning all missing mails");
206 | for (int i = 0; i < folder.Count; i += 100)
207 | {
208 | Console.WriteLine($"fatching mails {i}/{folder.Count}");
209 | int max = i + 100;
210 | if (max >= folder.Count)
211 | max = folder.Count - 1;
212 | foreach (var m in folder.Fetch(i, max, MessageSummaryItems.Full | MessageSummaryItems.UniqueId))
213 | {
214 | if (m.Envelope.Subject.IndexOf("[mailDisk]") == 0)
215 | {
216 | string name = m.Envelope.Subject.Substring("[mailDisk]".Length);
217 | Console.WriteLine($"checking file {name}");
218 | MatchCollection mc = Regex.Matches(name, @"(.+?)<(\d+?)/(\d+?)>");
219 | if (mc.Count > 0 && mc[0].Groups.Count == 4)
220 | name = mc[0].Groups[1].ToString();
221 | if (!files.Contains(name))
222 | {
223 | Console.WriteLine($"file {name} is not right, mark as deleted");
224 | folder.AddFlags(m.UniqueId, MessageFlags.Deleted, true);
225 | }
226 | }
227 | }
228 | if (max == folder.Count)
229 | break;
230 | }
231 | folder.Expunge();
232 | }
233 | client.Disconnect(true);
234 | return (string[])files.ToArray(typeof(string));
235 | }
236 |
237 | ///
238 | /// download file
239 | ///
240 | /// mail folder
241 | /// file save name
242 | public void DownloadFile(string folderPath, string fileName, string local,
243 | ImapClient client = null,
244 | IList all = null,
245 | IMailFolder folder = null)
246 | {
247 | if(File.Exists(local))
248 | {
249 | Console.WriteLine($"error! file {local} already exist!");
250 | return;
251 | }
252 | if(client == null)
253 | client = GetImapClient();
254 |
255 | var invalidFileName = Path.GetInvalidFileNameChars();
256 | fileName = invalidFileName.Aggregate(fileName, (o, r) => (o.Replace(r.ToString(), string.Empty)));
257 |
258 | if (all == null)
259 | {
260 | folder = client.GetFolder(folderPath);
261 | folder.Open(FolderAccess.ReadOnly);
262 | var uids = folder.Search(SearchQuery.SubjectContains($"[mailDisk]{fileName}"));
263 |
264 | Console.WriteLine($"find {uids.Count} matchs in this folder");
265 | Console.WriteLine($"fatching mails");
266 | all = folder.Fetch(uids, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);
267 | }
268 | bool singleFile = true;
269 | int fileSum = 0;
270 | bool hasFile = false;
271 | foreach(var m in all)
272 | {
273 | string subject = m.Envelope.Subject.Substring("[mailDisk]".Length);
274 | if (subject.IndexOf(fileName) == 0 && (subject.Length == fileName.Length || subject.Substring(fileName.Length, 1) == "<"))
275 | {
276 | if(subject.Length == fileName.Length)
277 | {
278 | hasFile = true;
279 | break;
280 | }
281 | MatchCollection mc = Regex.Matches(subject, @"<1/(\d+?)>");
282 | if (mc.Count > 0 && mc[0].Groups.Count == 2)
283 | {
284 | fileSum = int.Parse(mc[0].Groups[1].ToString());
285 | singleFile = false;
286 | hasFile = true;
287 | break;
288 | }
289 | }
290 | }
291 |
292 | if(!hasFile)
293 | {
294 | Console.WriteLine($"error! file not exist!");
295 | return;
296 | }
297 |
298 | if(singleFile)
299 | {
300 | foreach (var m in all)
301 | {
302 | if(m.Envelope.Subject.IndexOf($"[mailDisk]{fileName}") == 0)
303 | {
304 | while(true)
305 | {
306 | try
307 | {
308 | using (var output = File.Create(local))
309 | {
310 | var r = Download(folder, m.UniqueId);
311 | r.Position = 0;
312 | r.CopyTo(output);
313 | break;
314 | }
315 | }
316 | catch(Exception e)
317 | {
318 | Console.WriteLine($"[disk Download]fail, retry, infomation:" + e.Message);
319 | }
320 | }
321 | }
322 | }
323 | }
324 | else
325 | {
326 | ArrayList mails = new ArrayList();
327 | foreach (var m in all)
328 | {
329 | string subject = m.Envelope.Subject.Substring("[mailDisk]".Length);
330 | mails.Add(subject);
331 | }
332 | Console.WriteLine($"find file {fileName} with {fileSum} parts,checking...");
333 | bool result = true;//check is it have all files
334 | for (int i = 1; i <= fileSum; i++)
335 | {
336 | if (!mails.Contains($"{fileName}<{i}/{fileSum}>"))
337 | {
338 | result = false;
339 | break;
340 | }
341 | }
342 | if (result)
343 | {
344 | Console.WriteLine($"file {fileName} check ok, begin download...");
345 | using (var output = File.Create(local))
346 | {
347 | for (int i = 1; i <= fileSum; i++)
348 | {
349 | foreach (var m in all)
350 | {
351 | if (m.Envelope.Subject.IndexOf($"[mailDisk]{fileName}<{i}/{fileSum}>") == 0)
352 | {
353 | while (true)
354 | {
355 | try
356 | {
357 | Console.WriteLine($"downloading {fileName}<{i}/{fileSum}> ...");
358 | var r = Download(folder, m.UniqueId);
359 | r.Position = 0;
360 | r.CopyTo(output);
361 | break;
362 | }
363 | catch (Exception e)
364 | {
365 | Console.WriteLine($"[disk Download]fail, retry, infomation:" + e.Message);
366 | }
367 | }
368 | }
369 | }
370 | }
371 | }
372 | }
373 | else
374 | {
375 | Console.WriteLine($"file {fileName}'s parts are missing, download fail");
376 | return;
377 | }
378 | }
379 | Console.WriteLine($"file {fileName} download success!");
380 | }
381 |
382 | ///
383 | /// download a mail's attachments
384 | ///
385 | /// file folder
386 | /// mail uid
387 | /// file's stream
388 | private Stream Download(IMailFolder folder, UniqueId id)
389 | {
390 | Stream stream = new MemoryStream();
391 | MimeMessage message = folder.GetMessage(id);
392 | foreach (MimePart attachment in message.Attachments)
393 | {
394 | //下载附件
395 | using (var cancel = new CancellationTokenSource())
396 | {
397 | attachment.Content.DecodeTo(stream, cancel.Token);
398 | return stream;
399 | }
400 | }
401 | return stream;
402 | }
403 |
404 |
405 |
406 | ///
407 | /// upload big files, auto split by setting's size
408 | ///
409 | /// file name on cloud disk
410 | /// folder on email
411 | /// local file path
412 | /// max size for each mail
413 | /// file upload success or not
414 | public bool UploadBigFile(string fileName, string folderPath, string filePath, int blockSize)
415 | {
416 | if (!File.Exists(filePath))
417 | {
418 | Console.WriteLine($"error! file {filePath} not exist!");
419 | return false;
420 | }
421 | fileName = fileName.Replace("\\", "/");
422 | while(fileName.IndexOf("/") == 0)//remove the "/" head
423 | {
424 | fileName = fileName.Substring(1);
425 | }
426 |
427 | FileInfo fileInfo = new FileInfo(filePath);
428 | if (fileInfo.Length > blockSize)
429 | {
430 |
431 | Console.WriteLine($"[disk Upload]file need to be splited");
432 |
433 | var steps = (int)Math.Ceiling((double)fileInfo.Length / blockSize);
434 | using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
435 | {
436 | using (BinaryReader br = new BinaryReader(fs))
437 | {
438 | int couter = 1;
439 | bool isReadingComplete = false;
440 | while (!isReadingComplete)
441 | {
442 | string tempName = couter.ToString();
443 |
444 | byte[] bytes = br.ReadBytes(blockSize);
445 | Stream stream = new MemoryStream(bytes);
446 |
447 | bool result = false;
448 | while(!result)
449 | {
450 | try
451 | {
452 | result = Upload(fileName + $"<{couter}/{steps}>", folderPath, stream);
453 | }
454 | catch(Exception e)
455 | {
456 | Console.WriteLine($"[disk Upload]fail, retry, infomation:" + e.Message);
457 | }
458 | }
459 |
460 | isReadingComplete = (bytes.Length != blockSize);
461 | if (!isReadingComplete)
462 | {
463 | couter += 1;
464 | }
465 | }
466 | }
467 | }
468 | return true;
469 | }
470 | else
471 | {
472 | bool result = false;
473 | while (!result)
474 | {
475 | try
476 | {
477 | result = Upload(fileName, folderPath, File.OpenRead(filePath));
478 | }
479 | catch (Exception e)
480 | {
481 | Console.WriteLine($"[disk Upload]fail, retry, infomation:" + e.Message);
482 | }
483 | }
484 | return true;
485 | }
486 | }
487 |
488 |
489 | ///
490 | /// get all folders in this mail
491 | ///
492 | /// path, default is empty
493 | /// folder list
494 | public IMailFolder[] GetFolders(string path = "")
495 | {
496 | ArrayList folders = new ArrayList();
497 |
498 | var personal = GetImapClient().GetFolder(path);
499 |
500 | foreach (var folder in personal.GetSubfolders(false))
501 | {
502 | if (folder.GetSubfolders(false).Count > 0)
503 | {
504 | folders.Add(folder);
505 | folders.AddRange(GetFolders(folder.FullName));
506 | }
507 | else
508 | {
509 | folders.Add(folder);
510 | }
511 | }
512 | return (IMailFolder[])folders.ToArray(typeof(IMailFolder));
513 | }
514 |
515 |
516 | ///
517 | /// create a folder
518 | ///
519 | /// folder name and path
520 | /// result
521 | public void CreatFolder(string path)
522 | {
523 | var client = GetImapClient();
524 | if(path.IndexOf("/") < 0)
525 | {
526 | var root = client.GetFolder("");
527 | root.Create(path, true);
528 | }
529 | else
530 | {
531 | int l = path.LastIndexOf("/");
532 | var folder = client.GetFolder(path.Substring(0,l));
533 | folder.Create(path.Substring(l+1), true);
534 | }
535 | Console.WriteLine($"[disk folder]folder {path} is created.");
536 | }
537 |
538 | private ArrayList tempFiles = new ArrayList();
539 | public void RefreshFiles(string folderPath)
540 | {
541 | tempFiles.AddRange(GetFileList(folderPath));
542 | foreach (var s in tempFiles)
543 | {
544 | Console.WriteLine(s);
545 | }
546 | }
547 |
548 | ///
549 | /// upload a folder
550 | ///
551 | /// cloud folder path
552 | /// mail folder
553 | /// local folder path
554 | /// each mail size
555 | public void UploadFolder(string cloudPath, string folderPath, string localPath, int blockSize)
556 | {
557 | while (cloudPath.LastIndexOf("/") == cloudPath.Length - 1)//remove last "/"
558 | {
559 | cloudPath = cloudPath.Substring(0, cloudPath.Length - 1);
560 | }
561 | Console.WriteLine($"[disk upload folder]upload {localPath} to {cloudPath}");
562 | if (!Directory.Exists(localPath))
563 | {
564 | Console.WriteLine($"error! folder {localPath} not exist!");
565 | return;
566 | }
567 |
568 | foreach (var f in Directory.GetDirectories(localPath))
569 | {
570 | int l = f.LastIndexOf("/");
571 | if(l == -1)
572 | l = f.LastIndexOf("\\");
573 | UploadFolder(cloudPath + f.Substring(l), folderPath, f, blockSize);
574 | }
575 | foreach(var f in Directory.GetFiles(localPath))
576 | {
577 | int l = f.LastIndexOf("/");
578 | if (l == -1)
579 | l = f.LastIndexOf("\\");
580 |
581 | string fileName = cloudPath + f.Substring(l);
582 | fileName = fileName.Replace("\\", "/");
583 | while (fileName.IndexOf("/") == 0)//remove the "/" head
584 | {
585 | fileName = fileName.Substring(1);
586 | }
587 |
588 | if (tempFiles.Contains(fileName))
589 | Console.WriteLine($"[disk upload folder] file {fileName} is" +
590 | $" already exist in mail disk, skip upload.");
591 | else
592 | UploadBigFile(fileName, folderPath, f, blockSize);
593 | }
594 | }
595 |
596 | ///
597 | /// download a floder
598 | ///
599 | /// cloud folder path
600 | /// mail folder
601 | /// local folder path
602 | public void DownloadFolder(string cloudPath, string folderPath, string localPath)
603 | {
604 | while (cloudPath.LastIndexOf("/") == cloudPath.Length - 1)//remove last "/"
605 | {
606 | cloudPath = cloudPath.Substring(0, cloudPath.Length - 1);
607 | }
608 | cloudPath += "/";
609 | localPath = localPath.Replace("\\", "/");
610 | if (localPath.LastIndexOf("/") != localPath.Length - 1)
611 | localPath += "/";
612 | Console.WriteLine($"[disk download folder]download {cloudPath} to {localPath}");
613 |
614 | var client = GetImapClient();
615 | var folder = client.GetFolder(folderPath);
616 | folder.Open(FolderAccess.ReadOnly);
617 |
618 | Console.WriteLine($"find {folder.Count} files in this folder");
619 | Console.WriteLine($"fatching mails");
620 | var all = folder.Fetch(0,-1, MessageSummaryItems.Full | MessageSummaryItems.UniqueId);
621 |
622 | foreach (string f in GetFileList(folderPath))
623 | {
624 | if(f.IndexOf(cloudPath) == 0)//match folder
625 | {
626 | string localFile = localPath + f.Substring(cloudPath.Length);
627 | localPath = localPath.Replace("\\", "/");
628 | int l = localFile.LastIndexOf("/");
629 | Directory.CreateDirectory(localFile.Substring(0, l));
630 | DownloadFile(folderPath, f, localFile, client, all, folder);
631 | }
632 | }
633 | }
634 | }
635 | }
636 |
--------------------------------------------------------------------------------
/maildisk/maildisk/maildisk.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | chenxuuu
7 | chenxublog.com
8 | make your e-mail become a netdisk!
9 | 1.0.2.3
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/pictures/big.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenxuuu/Mail-Box-Net-Disk/dcb1222d01c5596fd6073898f37650a31f35292b/pictures/big.psd
--------------------------------------------------------------------------------
/pictures/mail.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenxuuu/Mail-Box-Net-Disk/dcb1222d01c5596fd6073898f37650a31f35292b/pictures/mail.psd
--------------------------------------------------------------------------------
/pictures/mailico.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenxuuu/Mail-Box-Net-Disk/dcb1222d01c5596fd6073898f37650a31f35292b/pictures/mailico.psd
--------------------------------------------------------------------------------