Merge lp:~jamesh/storage-provider-webdav/landing-20161118 into lp:storage-provider-webdav
- landing-20161118
- Merge into trunk
Proposed by
James Henstridge
Status: | Merged |
---|---|
Approved by: | Michi Henning |
Approved revision: | 21 |
Merged at revision: | 11 |
Proposed branch: | lp:~jamesh/storage-provider-webdav/landing-20161118 |
Merge into: | lp:storage-provider-webdav |
Diff against target: |
5576 lines (+4454/-257) 56 files modified
CMakeLists.txt (+3/-2) COPYING.GPL (+674/-0) COPYING.LGPL (+165/-0) data/CMakeLists.txt (+17/-0) data/com.canonical.StorageFramework.Provider.OwnCloud.service.in (+3/-0) data/storage-provider-owncloud.application (+12/-0) data/storage-provider-owncloud.desktop (+5/-0) data/storage-provider-owncloud.service (+8/-0) debian/control (+8/-7) debian/copyright (+36/-0) src/CMakeLists.txt (+17/-1) src/CopyMoveHandler.cpp (+85/-0) src/CopyMoveHandler.h (+57/-0) src/CreateFolderHandler.cpp (+75/-0) src/CreateFolderHandler.h (+53/-0) src/DavDownloadJob.cpp (+215/-0) src/DavDownloadJob.h (+72/-0) src/DavProvider.cpp (+183/-13) src/DavProvider.h (+35/-0) src/DavUploadJob.cpp (+140/-0) src/DavUploadJob.h (+65/-0) src/DeleteHandler.cpp (+62/-0) src/DeleteHandler.h (+49/-0) src/ListHandler.cpp (+59/-0) src/ListHandler.h (+43/-0) src/LookupHandler.cpp (+54/-0) src/LookupHandler.h (+42/-0) src/MetadataHandler.cpp (+61/-0) src/MetadataHandler.h (+42/-0) src/MultiStatusParser.cpp (+28/-4) src/MultiStatusParser.h (+18/-0) src/OwncloudProvider.cpp (+25/-3) src/OwncloudProvider.h (+18/-0) src/PropFindHandler.cpp (+127/-131) src/PropFindHandler.h (+43/-16) src/RetrieveMetadataHandler.cpp (+52/-0) src/RetrieveMetadataHandler.h (+45/-0) src/RootsHandler.cpp (+61/-0) src/RootsHandler.h (+41/-0) src/http_error.cpp (+181/-0) src/http_error.h (+30/-0) src/item_id.cpp (+59/-0) src/item_id.h (+23/-0) src/main.cpp (+19/-1) tests/CMakeLists.txt (+1/-6) tests/davprovider/davprovider_test.cpp (+843/-32) tests/http_error/CMakeLists.txt (+8/-0) tests/http_error/http_error_test.cpp (+278/-0) tests/item_id/item_id_test.cpp (+46/-0) tests/multistatus/multistatus_test.cpp (+24/-6) tests/utils/DavEnvironment.cpp (+18/-0) tests/utils/DavEnvironment.h (+18/-0) tests/utils/ProviderEnvironment.cpp (+27/-7) tests/utils/ProviderEnvironment.h (+25/-5) tests/utils/fake-online-accounts-daemon.py (+16/-22) tests/utils/sabredav-server.php (+40/-1) |
To merge this branch: | bzr merge lp:~jamesh/storage-provider-webdav/landing-20161118 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michi Henning (community) | Approve | ||
Review via email: mp+311240@code.launchpad.net |
Commit message
Merge lp:storage-provider-webdav/devel r22 to trunk.
Description of the change
Merge lp:storage-provider-webdav/devel r22 to trunk.
To post a comment you must log in.
- 21. By James Henstridge
-
Remove the item ID check in RetrievMetadata
Handler: :finish( ), since the WebDAV server may encode URIs differently to Qt. Approved by unity-api-1-bot, Michi Henning.
- 22. By James Henstridge
-
Update the test suite to use the v2 client API.
Approved by Michi Henning, unity-api-1-bot.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-09-27 07:08:19 +0000 |
3 | +++ CMakeLists.txt 2016-11-25 05:01:23 +0000 |
4 | @@ -16,12 +16,13 @@ |
5 | find_package(Qt5Network REQUIRED) |
6 | find_package(Qt5Test REQUIRED) |
7 | find_package(Qt5Xml REQUIRED) |
8 | -pkg_check_modules(SF_PROVIDER REQUIRED storage-framework-provider-1) |
9 | -pkg_check_modules(SF_CLIENT REQUIRED storage-framework-qt-client-1) |
10 | +pkg_check_modules(SF_PROVIDER REQUIRED storage-framework-provider-1>=0.2) |
11 | +pkg_check_modules(SF_CLIENT REQUIRED storage-framework-qt-client-2>=0.2) |
12 | |
13 | add_definitions(-DQT_NO_KEYWORDS) |
14 | |
15 | add_subdirectory(src) |
16 | +add_subdirectory(data) |
17 | add_subdirectory(tests) |
18 | |
19 | enable_coverage_report( |
20 | |
21 | === added file 'COPYING.GPL' |
22 | --- COPYING.GPL 1970-01-01 00:00:00 +0000 |
23 | +++ COPYING.GPL 2016-11-25 05:01:23 +0000 |
24 | @@ -0,0 +1,674 @@ |
25 | + GNU GENERAL PUBLIC LICENSE |
26 | + Version 3, 29 June 2007 |
27 | + |
28 | + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
29 | + Everyone is permitted to copy and distribute verbatim copies |
30 | + of this license document, but changing it is not allowed. |
31 | + |
32 | + Preamble |
33 | + |
34 | + The GNU General Public License is a free, copyleft license for |
35 | +software and other kinds of works. |
36 | + |
37 | + The licenses for most software and other practical works are designed |
38 | +to take away your freedom to share and change the works. By contrast, |
39 | +the GNU General Public License is intended to guarantee your freedom to |
40 | +share and change all versions of a program--to make sure it remains free |
41 | +software for all its users. We, the Free Software Foundation, use the |
42 | +GNU General Public License for most of our software; it applies also to |
43 | +any other work released this way by its authors. You can apply it to |
44 | +your programs, too. |
45 | + |
46 | + When we speak of free software, we are referring to freedom, not |
47 | +price. Our General Public Licenses are designed to make sure that you |
48 | +have the freedom to distribute copies of free software (and charge for |
49 | +them if you wish), that you receive source code or can get it if you |
50 | +want it, that you can change the software or use pieces of it in new |
51 | +free programs, and that you know you can do these things. |
52 | + |
53 | + To protect your rights, we need to prevent others from denying you |
54 | +these rights or asking you to surrender the rights. Therefore, you have |
55 | +certain responsibilities if you distribute copies of the software, or if |
56 | +you modify it: responsibilities to respect the freedom of others. |
57 | + |
58 | + For example, if you distribute copies of such a program, whether |
59 | +gratis or for a fee, you must pass on to the recipients the same |
60 | +freedoms that you received. You must make sure that they, too, receive |
61 | +or can get the source code. And you must show them these terms so they |
62 | +know their rights. |
63 | + |
64 | + Developers that use the GNU GPL protect your rights with two steps: |
65 | +(1) assert copyright on the software, and (2) offer you this License |
66 | +giving you legal permission to copy, distribute and/or modify it. |
67 | + |
68 | + For the developers' and authors' protection, the GPL clearly explains |
69 | +that there is no warranty for this free software. For both users' and |
70 | +authors' sake, the GPL requires that modified versions be marked as |
71 | +changed, so that their problems will not be attributed erroneously to |
72 | +authors of previous versions. |
73 | + |
74 | + Some devices are designed to deny users access to install or run |
75 | +modified versions of the software inside them, although the manufacturer |
76 | +can do so. This is fundamentally incompatible with the aim of |
77 | +protecting users' freedom to change the software. The systematic |
78 | +pattern of such abuse occurs in the area of products for individuals to |
79 | +use, which is precisely where it is most unacceptable. Therefore, we |
80 | +have designed this version of the GPL to prohibit the practice for those |
81 | +products. If such problems arise substantially in other domains, we |
82 | +stand ready to extend this provision to those domains in future versions |
83 | +of the GPL, as needed to protect the freedom of users. |
84 | + |
85 | + Finally, every program is threatened constantly by software patents. |
86 | +States should not allow patents to restrict development and use of |
87 | +software on general-purpose computers, but in those that do, we wish to |
88 | +avoid the special danger that patents applied to a free program could |
89 | +make it effectively proprietary. To prevent this, the GPL assures that |
90 | +patents cannot be used to render the program non-free. |
91 | + |
92 | + The precise terms and conditions for copying, distribution and |
93 | +modification follow. |
94 | + |
95 | + TERMS AND CONDITIONS |
96 | + |
97 | + 0. Definitions. |
98 | + |
99 | + "This License" refers to version 3 of the GNU General Public License. |
100 | + |
101 | + "Copyright" also means copyright-like laws that apply to other kinds of |
102 | +works, such as semiconductor masks. |
103 | + |
104 | + "The Program" refers to any copyrightable work licensed under this |
105 | +License. Each licensee is addressed as "you". "Licensees" and |
106 | +"recipients" may be individuals or organizations. |
107 | + |
108 | + To "modify" a work means to copy from or adapt all or part of the work |
109 | +in a fashion requiring copyright permission, other than the making of an |
110 | +exact copy. The resulting work is called a "modified version" of the |
111 | +earlier work or a work "based on" the earlier work. |
112 | + |
113 | + A "covered work" means either the unmodified Program or a work based |
114 | +on the Program. |
115 | + |
116 | + To "propagate" a work means to do anything with it that, without |
117 | +permission, would make you directly or secondarily liable for |
118 | +infringement under applicable copyright law, except executing it on a |
119 | +computer or modifying a private copy. Propagation includes copying, |
120 | +distribution (with or without modification), making available to the |
121 | +public, and in some countries other activities as well. |
122 | + |
123 | + To "convey" a work means any kind of propagation that enables other |
124 | +parties to make or receive copies. Mere interaction with a user through |
125 | +a computer network, with no transfer of a copy, is not conveying. |
126 | + |
127 | + An interactive user interface displays "Appropriate Legal Notices" |
128 | +to the extent that it includes a convenient and prominently visible |
129 | +feature that (1) displays an appropriate copyright notice, and (2) |
130 | +tells the user that there is no warranty for the work (except to the |
131 | +extent that warranties are provided), that licensees may convey the |
132 | +work under this License, and how to view a copy of this License. If |
133 | +the interface presents a list of user commands or options, such as a |
134 | +menu, a prominent item in the list meets this criterion. |
135 | + |
136 | + 1. Source Code. |
137 | + |
138 | + The "source code" for a work means the preferred form of the work |
139 | +for making modifications to it. "Object code" means any non-source |
140 | +form of a work. |
141 | + |
142 | + A "Standard Interface" means an interface that either is an official |
143 | +standard defined by a recognized standards body, or, in the case of |
144 | +interfaces specified for a particular programming language, one that |
145 | +is widely used among developers working in that language. |
146 | + |
147 | + The "System Libraries" of an executable work include anything, other |
148 | +than the work as a whole, that (a) is included in the normal form of |
149 | +packaging a Major Component, but which is not part of that Major |
150 | +Component, and (b) serves only to enable use of the work with that |
151 | +Major Component, or to implement a Standard Interface for which an |
152 | +implementation is available to the public in source code form. A |
153 | +"Major Component", in this context, means a major essential component |
154 | +(kernel, window system, and so on) of the specific operating system |
155 | +(if any) on which the executable work runs, or a compiler used to |
156 | +produce the work, or an object code interpreter used to run it. |
157 | + |
158 | + The "Corresponding Source" for a work in object code form means all |
159 | +the source code needed to generate, install, and (for an executable |
160 | +work) run the object code and to modify the work, including scripts to |
161 | +control those activities. However, it does not include the work's |
162 | +System Libraries, or general-purpose tools or generally available free |
163 | +programs which are used unmodified in performing those activities but |
164 | +which are not part of the work. For example, Corresponding Source |
165 | +includes interface definition files associated with source files for |
166 | +the work, and the source code for shared libraries and dynamically |
167 | +linked subprograms that the work is specifically designed to require, |
168 | +such as by intimate data communication or control flow between those |
169 | +subprograms and other parts of the work. |
170 | + |
171 | + The Corresponding Source need not include anything that users |
172 | +can regenerate automatically from other parts of the Corresponding |
173 | +Source. |
174 | + |
175 | + The Corresponding Source for a work in source code form is that |
176 | +same work. |
177 | + |
178 | + 2. Basic Permissions. |
179 | + |
180 | + All rights granted under this License are granted for the term of |
181 | +copyright on the Program, and are irrevocable provided the stated |
182 | +conditions are met. This License explicitly affirms your unlimited |
183 | +permission to run the unmodified Program. The output from running a |
184 | +covered work is covered by this License only if the output, given its |
185 | +content, constitutes a covered work. This License acknowledges your |
186 | +rights of fair use or other equivalent, as provided by copyright law. |
187 | + |
188 | + You may make, run and propagate covered works that you do not |
189 | +convey, without conditions so long as your license otherwise remains |
190 | +in force. You may convey covered works to others for the sole purpose |
191 | +of having them make modifications exclusively for you, or provide you |
192 | +with facilities for running those works, provided that you comply with |
193 | +the terms of this License in conveying all material for which you do |
194 | +not control copyright. Those thus making or running the covered works |
195 | +for you must do so exclusively on your behalf, under your direction |
196 | +and control, on terms that prohibit them from making any copies of |
197 | +your copyrighted material outside their relationship with you. |
198 | + |
199 | + Conveying under any other circumstances is permitted solely under |
200 | +the conditions stated below. Sublicensing is not allowed; section 10 |
201 | +makes it unnecessary. |
202 | + |
203 | + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
204 | + |
205 | + No covered work shall be deemed part of an effective technological |
206 | +measure under any applicable law fulfilling obligations under article |
207 | +11 of the WIPO copyright treaty adopted on 20 December 1996, or |
208 | +similar laws prohibiting or restricting circumvention of such |
209 | +measures. |
210 | + |
211 | + When you convey a covered work, you waive any legal power to forbid |
212 | +circumvention of technological measures to the extent such circumvention |
213 | +is effected by exercising rights under this License with respect to |
214 | +the covered work, and you disclaim any intention to limit operation or |
215 | +modification of the work as a means of enforcing, against the work's |
216 | +users, your or third parties' legal rights to forbid circumvention of |
217 | +technological measures. |
218 | + |
219 | + 4. Conveying Verbatim Copies. |
220 | + |
221 | + You may convey verbatim copies of the Program's source code as you |
222 | +receive it, in any medium, provided that you conspicuously and |
223 | +appropriately publish on each copy an appropriate copyright notice; |
224 | +keep intact all notices stating that this License and any |
225 | +non-permissive terms added in accord with section 7 apply to the code; |
226 | +keep intact all notices of the absence of any warranty; and give all |
227 | +recipients a copy of this License along with the Program. |
228 | + |
229 | + You may charge any price or no price for each copy that you convey, |
230 | +and you may offer support or warranty protection for a fee. |
231 | + |
232 | + 5. Conveying Modified Source Versions. |
233 | + |
234 | + You may convey a work based on the Program, or the modifications to |
235 | +produce it from the Program, in the form of source code under the |
236 | +terms of section 4, provided that you also meet all of these conditions: |
237 | + |
238 | + a) The work must carry prominent notices stating that you modified |
239 | + it, and giving a relevant date. |
240 | + |
241 | + b) The work must carry prominent notices stating that it is |
242 | + released under this License and any conditions added under section |
243 | + 7. This requirement modifies the requirement in section 4 to |
244 | + "keep intact all notices". |
245 | + |
246 | + c) You must license the entire work, as a whole, under this |
247 | + License to anyone who comes into possession of a copy. This |
248 | + License will therefore apply, along with any applicable section 7 |
249 | + additional terms, to the whole of the work, and all its parts, |
250 | + regardless of how they are packaged. This License gives no |
251 | + permission to license the work in any other way, but it does not |
252 | + invalidate such permission if you have separately received it. |
253 | + |
254 | + d) If the work has interactive user interfaces, each must display |
255 | + Appropriate Legal Notices; however, if the Program has interactive |
256 | + interfaces that do not display Appropriate Legal Notices, your |
257 | + work need not make them do so. |
258 | + |
259 | + A compilation of a covered work with other separate and independent |
260 | +works, which are not by their nature extensions of the covered work, |
261 | +and which are not combined with it such as to form a larger program, |
262 | +in or on a volume of a storage or distribution medium, is called an |
263 | +"aggregate" if the compilation and its resulting copyright are not |
264 | +used to limit the access or legal rights of the compilation's users |
265 | +beyond what the individual works permit. Inclusion of a covered work |
266 | +in an aggregate does not cause this License to apply to the other |
267 | +parts of the aggregate. |
268 | + |
269 | + 6. Conveying Non-Source Forms. |
270 | + |
271 | + You may convey a covered work in object code form under the terms |
272 | +of sections 4 and 5, provided that you also convey the |
273 | +machine-readable Corresponding Source under the terms of this License, |
274 | +in one of these ways: |
275 | + |
276 | + a) Convey the object code in, or embodied in, a physical product |
277 | + (including a physical distribution medium), accompanied by the |
278 | + Corresponding Source fixed on a durable physical medium |
279 | + customarily used for software interchange. |
280 | + |
281 | + b) Convey the object code in, or embodied in, a physical product |
282 | + (including a physical distribution medium), accompanied by a |
283 | + written offer, valid for at least three years and valid for as |
284 | + long as you offer spare parts or customer support for that product |
285 | + model, to give anyone who possesses the object code either (1) a |
286 | + copy of the Corresponding Source for all the software in the |
287 | + product that is covered by this License, on a durable physical |
288 | + medium customarily used for software interchange, for a price no |
289 | + more than your reasonable cost of physically performing this |
290 | + conveying of source, or (2) access to copy the |
291 | + Corresponding Source from a network server at no charge. |
292 | + |
293 | + c) Convey individual copies of the object code with a copy of the |
294 | + written offer to provide the Corresponding Source. This |
295 | + alternative is allowed only occasionally and noncommercially, and |
296 | + only if you received the object code with such an offer, in accord |
297 | + with subsection 6b. |
298 | + |
299 | + d) Convey the object code by offering access from a designated |
300 | + place (gratis or for a charge), and offer equivalent access to the |
301 | + Corresponding Source in the same way through the same place at no |
302 | + further charge. You need not require recipients to copy the |
303 | + Corresponding Source along with the object code. If the place to |
304 | + copy the object code is a network server, the Corresponding Source |
305 | + may be on a different server (operated by you or a third party) |
306 | + that supports equivalent copying facilities, provided you maintain |
307 | + clear directions next to the object code saying where to find the |
308 | + Corresponding Source. Regardless of what server hosts the |
309 | + Corresponding Source, you remain obligated to ensure that it is |
310 | + available for as long as needed to satisfy these requirements. |
311 | + |
312 | + e) Convey the object code using peer-to-peer transmission, provided |
313 | + you inform other peers where the object code and Corresponding |
314 | + Source of the work are being offered to the general public at no |
315 | + charge under subsection 6d. |
316 | + |
317 | + A separable portion of the object code, whose source code is excluded |
318 | +from the Corresponding Source as a System Library, need not be |
319 | +included in conveying the object code work. |
320 | + |
321 | + A "User Product" is either (1) a "consumer product", which means any |
322 | +tangible personal property which is normally used for personal, family, |
323 | +or household purposes, or (2) anything designed or sold for incorporation |
324 | +into a dwelling. In determining whether a product is a consumer product, |
325 | +doubtful cases shall be resolved in favor of coverage. For a particular |
326 | +product received by a particular user, "normally used" refers to a |
327 | +typical or common use of that class of product, regardless of the status |
328 | +of the particular user or of the way in which the particular user |
329 | +actually uses, or expects or is expected to use, the product. A product |
330 | +is a consumer product regardless of whether the product has substantial |
331 | +commercial, industrial or non-consumer uses, unless such uses represent |
332 | +the only significant mode of use of the product. |
333 | + |
334 | + "Installation Information" for a User Product means any methods, |
335 | +procedures, authorization keys, or other information required to install |
336 | +and execute modified versions of a covered work in that User Product from |
337 | +a modified version of its Corresponding Source. The information must |
338 | +suffice to ensure that the continued functioning of the modified object |
339 | +code is in no case prevented or interfered with solely because |
340 | +modification has been made. |
341 | + |
342 | + If you convey an object code work under this section in, or with, or |
343 | +specifically for use in, a User Product, and the conveying occurs as |
344 | +part of a transaction in which the right of possession and use of the |
345 | +User Product is transferred to the recipient in perpetuity or for a |
346 | +fixed term (regardless of how the transaction is characterized), the |
347 | +Corresponding Source conveyed under this section must be accompanied |
348 | +by the Installation Information. But this requirement does not apply |
349 | +if neither you nor any third party retains the ability to install |
350 | +modified object code on the User Product (for example, the work has |
351 | +been installed in ROM). |
352 | + |
353 | + The requirement to provide Installation Information does not include a |
354 | +requirement to continue to provide support service, warranty, or updates |
355 | +for a work that has been modified or installed by the recipient, or for |
356 | +the User Product in which it has been modified or installed. Access to a |
357 | +network may be denied when the modification itself materially and |
358 | +adversely affects the operation of the network or violates the rules and |
359 | +protocols for communication across the network. |
360 | + |
361 | + Corresponding Source conveyed, and Installation Information provided, |
362 | +in accord with this section must be in a format that is publicly |
363 | +documented (and with an implementation available to the public in |
364 | +source code form), and must require no special password or key for |
365 | +unpacking, reading or copying. |
366 | + |
367 | + 7. Additional Terms. |
368 | + |
369 | + "Additional permissions" are terms that supplement the terms of this |
370 | +License by making exceptions from one or more of its conditions. |
371 | +Additional permissions that are applicable to the entire Program shall |
372 | +be treated as though they were included in this License, to the extent |
373 | +that they are valid under applicable law. If additional permissions |
374 | +apply only to part of the Program, that part may be used separately |
375 | +under those permissions, but the entire Program remains governed by |
376 | +this License without regard to the additional permissions. |
377 | + |
378 | + When you convey a copy of a covered work, you may at your option |
379 | +remove any additional permissions from that copy, or from any part of |
380 | +it. (Additional permissions may be written to require their own |
381 | +removal in certain cases when you modify the work.) You may place |
382 | +additional permissions on material, added by you to a covered work, |
383 | +for which you have or can give appropriate copyright permission. |
384 | + |
385 | + Notwithstanding any other provision of this License, for material you |
386 | +add to a covered work, you may (if authorized by the copyright holders of |
387 | +that material) supplement the terms of this License with terms: |
388 | + |
389 | + a) Disclaiming warranty or limiting liability differently from the |
390 | + terms of sections 15 and 16 of this License; or |
391 | + |
392 | + b) Requiring preservation of specified reasonable legal notices or |
393 | + author attributions in that material or in the Appropriate Legal |
394 | + Notices displayed by works containing it; or |
395 | + |
396 | + c) Prohibiting misrepresentation of the origin of that material, or |
397 | + requiring that modified versions of such material be marked in |
398 | + reasonable ways as different from the original version; or |
399 | + |
400 | + d) Limiting the use for publicity purposes of names of licensors or |
401 | + authors of the material; or |
402 | + |
403 | + e) Declining to grant rights under trademark law for use of some |
404 | + trade names, trademarks, or service marks; or |
405 | + |
406 | + f) Requiring indemnification of licensors and authors of that |
407 | + material by anyone who conveys the material (or modified versions of |
408 | + it) with contractual assumptions of liability to the recipient, for |
409 | + any liability that these contractual assumptions directly impose on |
410 | + those licensors and authors. |
411 | + |
412 | + All other non-permissive additional terms are considered "further |
413 | +restrictions" within the meaning of section 10. If the Program as you |
414 | +received it, or any part of it, contains a notice stating that it is |
415 | +governed by this License along with a term that is a further |
416 | +restriction, you may remove that term. If a license document contains |
417 | +a further restriction but permits relicensing or conveying under this |
418 | +License, you may add to a covered work material governed by the terms |
419 | +of that license document, provided that the further restriction does |
420 | +not survive such relicensing or conveying. |
421 | + |
422 | + If you add terms to a covered work in accord with this section, you |
423 | +must place, in the relevant source files, a statement of the |
424 | +additional terms that apply to those files, or a notice indicating |
425 | +where to find the applicable terms. |
426 | + |
427 | + Additional terms, permissive or non-permissive, may be stated in the |
428 | +form of a separately written license, or stated as exceptions; |
429 | +the above requirements apply either way. |
430 | + |
431 | + 8. Termination. |
432 | + |
433 | + You may not propagate or modify a covered work except as expressly |
434 | +provided under this License. Any attempt otherwise to propagate or |
435 | +modify it is void, and will automatically terminate your rights under |
436 | +this License (including any patent licenses granted under the third |
437 | +paragraph of section 11). |
438 | + |
439 | + However, if you cease all violation of this License, then your |
440 | +license from a particular copyright holder is reinstated (a) |
441 | +provisionally, unless and until the copyright holder explicitly and |
442 | +finally terminates your license, and (b) permanently, if the copyright |
443 | +holder fails to notify you of the violation by some reasonable means |
444 | +prior to 60 days after the cessation. |
445 | + |
446 | + Moreover, your license from a particular copyright holder is |
447 | +reinstated permanently if the copyright holder notifies you of the |
448 | +violation by some reasonable means, this is the first time you have |
449 | +received notice of violation of this License (for any work) from that |
450 | +copyright holder, and you cure the violation prior to 30 days after |
451 | +your receipt of the notice. |
452 | + |
453 | + Termination of your rights under this section does not terminate the |
454 | +licenses of parties who have received copies or rights from you under |
455 | +this License. If your rights have been terminated and not permanently |
456 | +reinstated, you do not qualify to receive new licenses for the same |
457 | +material under section 10. |
458 | + |
459 | + 9. Acceptance Not Required for Having Copies. |
460 | + |
461 | + You are not required to accept this License in order to receive or |
462 | +run a copy of the Program. Ancillary propagation of a covered work |
463 | +occurring solely as a consequence of using peer-to-peer transmission |
464 | +to receive a copy likewise does not require acceptance. However, |
465 | +nothing other than this License grants you permission to propagate or |
466 | +modify any covered work. These actions infringe copyright if you do |
467 | +not accept this License. Therefore, by modifying or propagating a |
468 | +covered work, you indicate your acceptance of this License to do so. |
469 | + |
470 | + 10. Automatic Licensing of Downstream Recipients. |
471 | + |
472 | + Each time you convey a covered work, the recipient automatically |
473 | +receives a license from the original licensors, to run, modify and |
474 | +propagate that work, subject to this License. You are not responsible |
475 | +for enforcing compliance by third parties with this License. |
476 | + |
477 | + An "entity transaction" is a transaction transferring control of an |
478 | +organization, or substantially all assets of one, or subdividing an |
479 | +organization, or merging organizations. If propagation of a covered |
480 | +work results from an entity transaction, each party to that |
481 | +transaction who receives a copy of the work also receives whatever |
482 | +licenses to the work the party's predecessor in interest had or could |
483 | +give under the previous paragraph, plus a right to possession of the |
484 | +Corresponding Source of the work from the predecessor in interest, if |
485 | +the predecessor has it or can get it with reasonable efforts. |
486 | + |
487 | + You may not impose any further restrictions on the exercise of the |
488 | +rights granted or affirmed under this License. For example, you may |
489 | +not impose a license fee, royalty, or other charge for exercise of |
490 | +rights granted under this License, and you may not initiate litigation |
491 | +(including a cross-claim or counterclaim in a lawsuit) alleging that |
492 | +any patent claim is infringed by making, using, selling, offering for |
493 | +sale, or importing the Program or any portion of it. |
494 | + |
495 | + 11. Patents. |
496 | + |
497 | + A "contributor" is a copyright holder who authorizes use under this |
498 | +License of the Program or a work on which the Program is based. The |
499 | +work thus licensed is called the contributor's "contributor version". |
500 | + |
501 | + A contributor's "essential patent claims" are all patent claims |
502 | +owned or controlled by the contributor, whether already acquired or |
503 | +hereafter acquired, that would be infringed by some manner, permitted |
504 | +by this License, of making, using, or selling its contributor version, |
505 | +but do not include claims that would be infringed only as a |
506 | +consequence of further modification of the contributor version. For |
507 | +purposes of this definition, "control" includes the right to grant |
508 | +patent sublicenses in a manner consistent with the requirements of |
509 | +this License. |
510 | + |
511 | + Each contributor grants you a non-exclusive, worldwide, royalty-free |
512 | +patent license under the contributor's essential patent claims, to |
513 | +make, use, sell, offer for sale, import and otherwise run, modify and |
514 | +propagate the contents of its contributor version. |
515 | + |
516 | + In the following three paragraphs, a "patent license" is any express |
517 | +agreement or commitment, however denominated, not to enforce a patent |
518 | +(such as an express permission to practice a patent or covenant not to |
519 | +sue for patent infringement). To "grant" such a patent license to a |
520 | +party means to make such an agreement or commitment not to enforce a |
521 | +patent against the party. |
522 | + |
523 | + If you convey a covered work, knowingly relying on a patent license, |
524 | +and the Corresponding Source of the work is not available for anyone |
525 | +to copy, free of charge and under the terms of this License, through a |
526 | +publicly available network server or other readily accessible means, |
527 | +then you must either (1) cause the Corresponding Source to be so |
528 | +available, or (2) arrange to deprive yourself of the benefit of the |
529 | +patent license for this particular work, or (3) arrange, in a manner |
530 | +consistent with the requirements of this License, to extend the patent |
531 | +license to downstream recipients. "Knowingly relying" means you have |
532 | +actual knowledge that, but for the patent license, your conveying the |
533 | +covered work in a country, or your recipient's use of the covered work |
534 | +in a country, would infringe one or more identifiable patents in that |
535 | +country that you have reason to believe are valid. |
536 | + |
537 | + If, pursuant to or in connection with a single transaction or |
538 | +arrangement, you convey, or propagate by procuring conveyance of, a |
539 | +covered work, and grant a patent license to some of the parties |
540 | +receiving the covered work authorizing them to use, propagate, modify |
541 | +or convey a specific copy of the covered work, then the patent license |
542 | +you grant is automatically extended to all recipients of the covered |
543 | +work and works based on it. |
544 | + |
545 | + A patent license is "discriminatory" if it does not include within |
546 | +the scope of its coverage, prohibits the exercise of, or is |
547 | +conditioned on the non-exercise of one or more of the rights that are |
548 | +specifically granted under this License. You may not convey a covered |
549 | +work if you are a party to an arrangement with a third party that is |
550 | +in the business of distributing software, under which you make payment |
551 | +to the third party based on the extent of your activity of conveying |
552 | +the work, and under which the third party grants, to any of the |
553 | +parties who would receive the covered work from you, a discriminatory |
554 | +patent license (a) in connection with copies of the covered work |
555 | +conveyed by you (or copies made from those copies), or (b) primarily |
556 | +for and in connection with specific products or compilations that |
557 | +contain the covered work, unless you entered into that arrangement, |
558 | +or that patent license was granted, prior to 28 March 2007. |
559 | + |
560 | + Nothing in this License shall be construed as excluding or limiting |
561 | +any implied license or other defenses to infringement that may |
562 | +otherwise be available to you under applicable patent law. |
563 | + |
564 | + 12. No Surrender of Others' Freedom. |
565 | + |
566 | + If conditions are imposed on you (whether by court order, agreement or |
567 | +otherwise) that contradict the conditions of this License, they do not |
568 | +excuse you from the conditions of this License. If you cannot convey a |
569 | +covered work so as to satisfy simultaneously your obligations under this |
570 | +License and any other pertinent obligations, then as a consequence you may |
571 | +not convey it at all. For example, if you agree to terms that obligate you |
572 | +to collect a royalty for further conveying from those to whom you convey |
573 | +the Program, the only way you could satisfy both those terms and this |
574 | +License would be to refrain entirely from conveying the Program. |
575 | + |
576 | + 13. Use with the GNU Affero General Public License. |
577 | + |
578 | + Notwithstanding any other provision of this License, you have |
579 | +permission to link or combine any covered work with a work licensed |
580 | +under version 3 of the GNU Affero General Public License into a single |
581 | +combined work, and to convey the resulting work. The terms of this |
582 | +License will continue to apply to the part which is the covered work, |
583 | +but the special requirements of the GNU Affero General Public License, |
584 | +section 13, concerning interaction through a network will apply to the |
585 | +combination as such. |
586 | + |
587 | + 14. Revised Versions of this License. |
588 | + |
589 | + The Free Software Foundation may publish revised and/or new versions of |
590 | +the GNU General Public License from time to time. Such new versions will |
591 | +be similar in spirit to the present version, but may differ in detail to |
592 | +address new problems or concerns. |
593 | + |
594 | + Each version is given a distinguishing version number. If the |
595 | +Program specifies that a certain numbered version of the GNU General |
596 | +Public License "or any later version" applies to it, you have the |
597 | +option of following the terms and conditions either of that numbered |
598 | +version or of any later version published by the Free Software |
599 | +Foundation. If the Program does not specify a version number of the |
600 | +GNU General Public License, you may choose any version ever published |
601 | +by the Free Software Foundation. |
602 | + |
603 | + If the Program specifies that a proxy can decide which future |
604 | +versions of the GNU General Public License can be used, that proxy's |
605 | +public statement of acceptance of a version permanently authorizes you |
606 | +to choose that version for the Program. |
607 | + |
608 | + Later license versions may give you additional or different |
609 | +permissions. However, no additional obligations are imposed on any |
610 | +author or copyright holder as a result of your choosing to follow a |
611 | +later version. |
612 | + |
613 | + 15. Disclaimer of Warranty. |
614 | + |
615 | + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
616 | +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
617 | +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
618 | +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
619 | +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
620 | +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
621 | +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
622 | +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
623 | + |
624 | + 16. Limitation of Liability. |
625 | + |
626 | + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
627 | +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
628 | +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
629 | +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
630 | +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
631 | +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
632 | +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
633 | +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
634 | +SUCH DAMAGES. |
635 | + |
636 | + 17. Interpretation of Sections 15 and 16. |
637 | + |
638 | + If the disclaimer of warranty and limitation of liability provided |
639 | +above cannot be given local legal effect according to their terms, |
640 | +reviewing courts shall apply local law that most closely approximates |
641 | +an absolute waiver of all civil liability in connection with the |
642 | +Program, unless a warranty or assumption of liability accompanies a |
643 | +copy of the Program in return for a fee. |
644 | + |
645 | + END OF TERMS AND CONDITIONS |
646 | + |
647 | + How to Apply These Terms to Your New Programs |
648 | + |
649 | + If you develop a new program, and you want it to be of the greatest |
650 | +possible use to the public, the best way to achieve this is to make it |
651 | +free software which everyone can redistribute and change under these terms. |
652 | + |
653 | + To do so, attach the following notices to the program. It is safest |
654 | +to attach them to the start of each source file to most effectively |
655 | +state the exclusion of warranty; and each file should have at least |
656 | +the "copyright" line and a pointer to where the full notice is found. |
657 | + |
658 | + <one line to give the program's name and a brief idea of what it does.> |
659 | + Copyright (C) <year> <name of author> |
660 | + |
661 | + This program is free software: you can redistribute it and/or modify |
662 | + it under the terms of the GNU General Public License as published by |
663 | + the Free Software Foundation, either version 3 of the License, or |
664 | + (at your option) any later version. |
665 | + |
666 | + This program is distributed in the hope that it will be useful, |
667 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
668 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
669 | + GNU General Public License for more details. |
670 | + |
671 | + You should have received a copy of the GNU General Public License |
672 | + along with this program. If not, see <http://www.gnu.org/licenses/>. |
673 | + |
674 | +Also add information on how to contact you by electronic and paper mail. |
675 | + |
676 | + If the program does terminal interaction, make it output a short |
677 | +notice like this when it starts in an interactive mode: |
678 | + |
679 | + <program> Copyright (C) <year> <name of author> |
680 | + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
681 | + This is free software, and you are welcome to redistribute it |
682 | + under certain conditions; type `show c' for details. |
683 | + |
684 | +The hypothetical commands `show w' and `show c' should show the appropriate |
685 | +parts of the General Public License. Of course, your program's commands |
686 | +might be different; for a GUI interface, you would use an "about box". |
687 | + |
688 | + You should also get your employer (if you work as a programmer) or school, |
689 | +if any, to sign a "copyright disclaimer" for the program, if necessary. |
690 | +For more information on this, and how to apply and follow the GNU GPL, see |
691 | +<http://www.gnu.org/licenses/>. |
692 | + |
693 | + The GNU General Public License does not permit incorporating your program |
694 | +into proprietary programs. If your program is a subroutine library, you |
695 | +may consider it more useful to permit linking proprietary applications with |
696 | +the library. If this is what you want to do, use the GNU Lesser General |
697 | +Public License instead of this License. But first, please read |
698 | +<http://www.gnu.org/philosophy/why-not-lgpl.html>. |
699 | |
700 | === added file 'COPYING.LGPL' |
701 | --- COPYING.LGPL 1970-01-01 00:00:00 +0000 |
702 | +++ COPYING.LGPL 2016-11-25 05:01:23 +0000 |
703 | @@ -0,0 +1,165 @@ |
704 | + GNU LESSER GENERAL PUBLIC LICENSE |
705 | + Version 3, 29 June 2007 |
706 | + |
707 | + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
708 | + Everyone is permitted to copy and distribute verbatim copies |
709 | + of this license document, but changing it is not allowed. |
710 | + |
711 | + |
712 | + This version of the GNU Lesser General Public License incorporates |
713 | +the terms and conditions of version 3 of the GNU General Public |
714 | +License, supplemented by the additional permissions listed below. |
715 | + |
716 | + 0. Additional Definitions. |
717 | + |
718 | + As used herein, "this License" refers to version 3 of the GNU Lesser |
719 | +General Public License, and the "GNU GPL" refers to version 3 of the GNU |
720 | +General Public License. |
721 | + |
722 | + "The Library" refers to a covered work governed by this License, |
723 | +other than an Application or a Combined Work as defined below. |
724 | + |
725 | + An "Application" is any work that makes use of an interface provided |
726 | +by the Library, but which is not otherwise based on the Library. |
727 | +Defining a subclass of a class defined by the Library is deemed a mode |
728 | +of using an interface provided by the Library. |
729 | + |
730 | + A "Combined Work" is a work produced by combining or linking an |
731 | +Application with the Library. The particular version of the Library |
732 | +with which the Combined Work was made is also called the "Linked |
733 | +Version". |
734 | + |
735 | + The "Minimal Corresponding Source" for a Combined Work means the |
736 | +Corresponding Source for the Combined Work, excluding any source code |
737 | +for portions of the Combined Work that, considered in isolation, are |
738 | +based on the Application, and not on the Linked Version. |
739 | + |
740 | + The "Corresponding Application Code" for a Combined Work means the |
741 | +object code and/or source code for the Application, including any data |
742 | +and utility programs needed for reproducing the Combined Work from the |
743 | +Application, but excluding the System Libraries of the Combined Work. |
744 | + |
745 | + 1. Exception to Section 3 of the GNU GPL. |
746 | + |
747 | + You may convey a covered work under sections 3 and 4 of this License |
748 | +without being bound by section 3 of the GNU GPL. |
749 | + |
750 | + 2. Conveying Modified Versions. |
751 | + |
752 | + If you modify a copy of the Library, and, in your modifications, a |
753 | +facility refers to a function or data to be supplied by an Application |
754 | +that uses the facility (other than as an argument passed when the |
755 | +facility is invoked), then you may convey a copy of the modified |
756 | +version: |
757 | + |
758 | + a) under this License, provided that you make a good faith effort to |
759 | + ensure that, in the event an Application does not supply the |
760 | + function or data, the facility still operates, and performs |
761 | + whatever part of its purpose remains meaningful, or |
762 | + |
763 | + b) under the GNU GPL, with none of the additional permissions of |
764 | + this License applicable to that copy. |
765 | + |
766 | + 3. Object Code Incorporating Material from Library Header Files. |
767 | + |
768 | + The object code form of an Application may incorporate material from |
769 | +a header file that is part of the Library. You may convey such object |
770 | +code under terms of your choice, provided that, if the incorporated |
771 | +material is not limited to numerical parameters, data structure |
772 | +layouts and accessors, or small macros, inline functions and templates |
773 | +(ten or fewer lines in length), you do both of the following: |
774 | + |
775 | + a) Give prominent notice with each copy of the object code that the |
776 | + Library is used in it and that the Library and its use are |
777 | + covered by this License. |
778 | + |
779 | + b) Accompany the object code with a copy of the GNU GPL and this license |
780 | + document. |
781 | + |
782 | + 4. Combined Works. |
783 | + |
784 | + You may convey a Combined Work under terms of your choice that, |
785 | +taken together, effectively do not restrict modification of the |
786 | +portions of the Library contained in the Combined Work and reverse |
787 | +engineering for debugging such modifications, if you also do each of |
788 | +the following: |
789 | + |
790 | + a) Give prominent notice with each copy of the Combined Work that |
791 | + the Library is used in it and that the Library and its use are |
792 | + covered by this License. |
793 | + |
794 | + b) Accompany the Combined Work with a copy of the GNU GPL and this license |
795 | + document. |
796 | + |
797 | + c) For a Combined Work that displays copyright notices during |
798 | + execution, include the copyright notice for the Library among |
799 | + these notices, as well as a reference directing the user to the |
800 | + copies of the GNU GPL and this license document. |
801 | + |
802 | + d) Do one of the following: |
803 | + |
804 | + 0) Convey the Minimal Corresponding Source under the terms of this |
805 | + License, and the Corresponding Application Code in a form |
806 | + suitable for, and under terms that permit, the user to |
807 | + recombine or relink the Application with a modified version of |
808 | + the Linked Version to produce a modified Combined Work, in the |
809 | + manner specified by section 6 of the GNU GPL for conveying |
810 | + Corresponding Source. |
811 | + |
812 | + 1) Use a suitable shared library mechanism for linking with the |
813 | + Library. A suitable mechanism is one that (a) uses at run time |
814 | + a copy of the Library already present on the user's computer |
815 | + system, and (b) will operate properly with a modified version |
816 | + of the Library that is interface-compatible with the Linked |
817 | + Version. |
818 | + |
819 | + e) Provide Installation Information, but only if you would otherwise |
820 | + be required to provide such information under section 6 of the |
821 | + GNU GPL, and only to the extent that such information is |
822 | + necessary to install and execute a modified version of the |
823 | + Combined Work produced by recombining or relinking the |
824 | + Application with a modified version of the Linked Version. (If |
825 | + you use option 4d0, the Installation Information must accompany |
826 | + the Minimal Corresponding Source and Corresponding Application |
827 | + Code. If you use option 4d1, you must provide the Installation |
828 | + Information in the manner specified by section 6 of the GNU GPL |
829 | + for conveying Corresponding Source.) |
830 | + |
831 | + 5. Combined Libraries. |
832 | + |
833 | + You may place library facilities that are a work based on the |
834 | +Library side by side in a single library together with other library |
835 | +facilities that are not Applications and are not covered by this |
836 | +License, and convey such a combined library under terms of your |
837 | +choice, if you do both of the following: |
838 | + |
839 | + a) Accompany the combined library with a copy of the same work based |
840 | + on the Library, uncombined with any other library facilities, |
841 | + conveyed under the terms of this License. |
842 | + |
843 | + b) Give prominent notice with the combined library that part of it |
844 | + is a work based on the Library, and explaining where to find the |
845 | + accompanying uncombined form of the same work. |
846 | + |
847 | + 6. Revised Versions of the GNU Lesser General Public License. |
848 | + |
849 | + The Free Software Foundation may publish revised and/or new versions |
850 | +of the GNU Lesser General Public License from time to time. Such new |
851 | +versions will be similar in spirit to the present version, but may |
852 | +differ in detail to address new problems or concerns. |
853 | + |
854 | + Each version is given a distinguishing version number. If the |
855 | +Library as you received it specifies that a certain numbered version |
856 | +of the GNU Lesser General Public License "or any later version" |
857 | +applies to it, you have the option of following the terms and |
858 | +conditions either of that published version or of any later version |
859 | +published by the Free Software Foundation. If the Library as you |
860 | +received it does not specify a version number of the GNU Lesser |
861 | +General Public License, you may choose any version of the GNU Lesser |
862 | +General Public License ever published by the Free Software Foundation. |
863 | + |
864 | + If the Library as you received it specifies that a proxy can decide |
865 | +whether future versions of the GNU Lesser General Public License shall |
866 | +apply, that proxy's public statement of acceptance of any version is |
867 | +permanent authorization for you to choose that version for the |
868 | +Library. |
869 | |
870 | === added directory 'data' |
871 | === added file 'data/CMakeLists.txt' |
872 | --- data/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
873 | +++ data/CMakeLists.txt 2016-11-25 05:01:23 +0000 |
874 | @@ -0,0 +1,17 @@ |
875 | +# OnlineAccounts support files |
876 | +install(FILES storage-provider-owncloud.service |
877 | + DESTINATION "${CMAKE_INSTALL_DATADIR}/accounts/services") |
878 | +install(FILES storage-provider-owncloud.application |
879 | + DESTINATION "${CMAKE_INSTALL_DATADIR}/accounts/applications") |
880 | +install(FILES storage-provider-owncloud.desktop |
881 | + DESTINATION "${CMAKE_INSTALL_DATADIR}/applications") |
882 | + |
883 | +# D-Bus serice activation |
884 | +configure_file( |
885 | + com.canonical.StorageFramework.Provider.OwnCloud.service.in |
886 | + com.canonical.StorageFramework.Provider.OwnCloud.service |
887 | +) |
888 | +install( |
889 | + FILES ${CMAKE_CURRENT_BINARY_DIR}/com.canonical.StorageFramework.Provider.OwnCloud.service |
890 | + DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services |
891 | +) |
892 | |
893 | === added file 'data/com.canonical.StorageFramework.Provider.OwnCloud.service.in' |
894 | --- data/com.canonical.StorageFramework.Provider.OwnCloud.service.in 1970-01-01 00:00:00 +0000 |
895 | +++ data/com.canonical.StorageFramework.Provider.OwnCloud.service.in 2016-11-25 05:01:23 +0000 |
896 | @@ -0,0 +1,3 @@ |
897 | +[D-BUS Service] |
898 | +Name=com.canonical.StorageFramework.Provider.OwnCloud |
899 | +Exec=@CMAKE_INSTALL_FULL_LIBDIR@/@PROJECT_NAME@/storage-provider-owncloud |
900 | |
901 | === added file 'data/storage-provider-owncloud.application' |
902 | --- data/storage-provider-owncloud.application 1970-01-01 00:00:00 +0000 |
903 | +++ data/storage-provider-owncloud.application 2016-11-25 05:01:23 +0000 |
904 | @@ -0,0 +1,12 @@ |
905 | +<?xml version="1.0" encoding="UTF-8" ?> |
906 | +<!-- -*- mode: nxml -*- --> |
907 | +<application id="storage-provider-owncloud"> |
908 | + <description>OwnCloud storage provider</description> |
909 | + <desktop-entry>storage-provider-owncloud.desktop</desktop-entry> |
910 | + |
911 | + <services> |
912 | + <service id="storage-provider-owncloud"> |
913 | + <description>Access your Owncloud documents</description> |
914 | + </service> |
915 | + </services> |
916 | +</application> |
917 | |
918 | === added file 'data/storage-provider-owncloud.desktop' |
919 | --- data/storage-provider-owncloud.desktop 1970-01-01 00:00:00 +0000 |
920 | +++ data/storage-provider-owncloud.desktop 2016-11-25 05:01:23 +0000 |
921 | @@ -0,0 +1,5 @@ |
922 | +[Desktop Entry] |
923 | +Type=Application |
924 | +NoDisplay=true |
925 | +Name=OwnCloud storage provider |
926 | +Icon=online-accounts-owncloud |
927 | |
928 | === added file 'data/storage-provider-owncloud.service' |
929 | --- data/storage-provider-owncloud.service 1970-01-01 00:00:00 +0000 |
930 | +++ data/storage-provider-owncloud.service 2016-11-25 05:01:23 +0000 |
931 | @@ -0,0 +1,8 @@ |
932 | +<?xml version="1.0" encoding="UTF-8" ?> |
933 | +<!-- -*- mode: nxml -*- --> |
934 | +<service id="storage-provider-owncloud"> |
935 | + <type>storage-provider</type> |
936 | + <name>OwnCloud</name> |
937 | + <icon>online-accounts-owncloud</icon> |
938 | + <provider>owncloud</provider> |
939 | +</service> |
940 | |
941 | === modified file 'debian/control' |
942 | --- debian/control 2016-09-27 07:08:19 +0000 |
943 | +++ debian/control 2016-11-25 05:01:23 +0000 |
944 | @@ -1,8 +1,8 @@ |
945 | Source: storage-provider-webdav |
946 | -Section: ??? |
947 | +Section: net |
948 | Priority: optional |
949 | -Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
950 | -Standards-Version: 3.9.6 |
951 | +Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
952 | +Standards-Version: 3.9.8 |
953 | Build-Depends: cmake, |
954 | cmake-extras (>= 0.4), |
955 | debhelper (>= 9), |
956 | @@ -12,8 +12,9 @@ |
957 | libgtest-dev, |
958 | libonline-accounts-qt-dev, |
959 | libqtdbustest1-dev, |
960 | - storage-framework-provider-dev, |
961 | - storage-framework-client-dev, |
962 | + libstorage-framework-qt-client-2-0, |
963 | + storage-framework-provider-dev (>= 0.2), |
964 | + storage-framework-client-dev (>= 0.2), |
965 | php-cli | php5-cli <!nocheck>, |
966 | php-sabre-dav <!nocheck>, |
967 | pkg-config, |
968 | @@ -31,8 +32,8 @@ |
969 | Architecture: any |
970 | Multi-Arch: foreign |
971 | Pre-Depends: ${misc:Pre-Depends}, |
972 | -Depends: ${misc:Depends}, |
973 | +Depends: account-plugin-owncloud, |
974 | + ${misc:Depends}, |
975 | ${shlibs:Depends}, |
976 | - account-plugin-owncloud |
977 | Description: Owncloud plugin for Storage Framework |
978 | A Storage Framework plugin providing access to Owncloud servers. |
979 | |
980 | === added file 'debian/copyright' |
981 | --- debian/copyright 1970-01-01 00:00:00 +0000 |
982 | +++ debian/copyright 2016-11-25 05:01:23 +0000 |
983 | @@ -0,0 +1,36 @@ |
984 | +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
985 | +Upstream-Name: storage-provider-webdav |
986 | +Source: https://launchpad.net/storage-provider-webdav |
987 | + |
988 | +Files: * |
989 | +Copyright: 2016 Canonical Ltd. |
990 | +License: GPL-3 |
991 | + This program is free software: you can redistribute it and/or modify |
992 | + it under the terms of the GNU General Public License version 3 as |
993 | + published by the Free Software Foundation. |
994 | + . |
995 | + This program is distributed in the hope that it will be useful, |
996 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
997 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
998 | + GNU General Public License for more details. |
999 | + . |
1000 | + On Debian/Ubuntu systems, the full text of the GPL v3 can be found in |
1001 | + `/usr/share/common-licenses/GPL-3' |
1002 | + |
1003 | +Files: tests/utils/DBusEnvironment.* |
1004 | + tests/utils/ProviderEnvironment.* |
1005 | + tests/utils/fake-onlye-accounts-daemon.py |
1006 | +Copyright: 2016 Canonical Ltd. |
1007 | +License: LGPL-3 |
1008 | + This program is free software: you can redistribute it and/or modify |
1009 | + it under the terms of version 3 of the GNU Lesser General Public |
1010 | + License as published by the Free Software Foundation. |
1011 | + . |
1012 | + This program is distributed in the hope that it will be useful, |
1013 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
1014 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1015 | + GNU Lesser General Public License for more details. |
1016 | + . |
1017 | + On Debian systems, the full text of the GNU Lesser General Public |
1018 | + License version 3 can be found in the file |
1019 | + `/usr/share/common-licenses/LGPL-3' |
1020 | |
1021 | === modified file 'src/CMakeLists.txt' |
1022 | --- src/CMakeLists.txt 2016-09-27 07:08:19 +0000 |
1023 | +++ src/CMakeLists.txt 2016-11-25 05:01:23 +0000 |
1024 | @@ -1,9 +1,20 @@ |
1025 | |
1026 | add_library(dav-provider-lib STATIC |
1027 | DavProvider.cpp |
1028 | - RootsHandler.cpp |
1029 | + DavDownloadJob.cpp |
1030 | + DavUploadJob.cpp |
1031 | MultiStatusParser.cpp |
1032 | + http_error.cpp |
1033 | item_id.cpp |
1034 | + CopyMoveHandler.cpp |
1035 | + CreateFolderHandler.cpp |
1036 | + DeleteHandler.cpp |
1037 | + PropFindHandler.cpp |
1038 | + ListHandler.cpp |
1039 | + LookupHandler.cpp |
1040 | + MetadataHandler.cpp |
1041 | + RetrieveMetadataHandler.cpp |
1042 | + RootsHandler.cpp |
1043 | ) |
1044 | target_compile_options(dav-provider-lib PUBLIC |
1045 | ${SF_PROVIDER_CFLAGS} |
1046 | @@ -28,3 +39,8 @@ |
1047 | target_link_libraries(storage-provider-owncloud |
1048 | dav-provider-lib |
1049 | ) |
1050 | + |
1051 | +install( |
1052 | + TARGETS storage-provider-owncloud |
1053 | + RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}" |
1054 | +) |
1055 | |
1056 | === added file 'src/CopyMoveHandler.cpp' |
1057 | --- src/CopyMoveHandler.cpp 1970-01-01 00:00:00 +0000 |
1058 | +++ src/CopyMoveHandler.cpp 2016-11-25 05:01:23 +0000 |
1059 | @@ -0,0 +1,85 @@ |
1060 | +/* |
1061 | + * Copyright (C) 2016 Canonical Ltd. |
1062 | + * |
1063 | + * This program is free software: you can redistribute it and/or modify |
1064 | + * it under the terms of the GNU General Public License version 3 as |
1065 | + * published by the Free Software Foundation. |
1066 | + * |
1067 | + * This program is distributed in the hope that it will be useful, |
1068 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1069 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1070 | + * GNU General Public License for more details. |
1071 | + * |
1072 | + * You should have received a copy of the GNU General Public License |
1073 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1074 | + * |
1075 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1076 | + */ |
1077 | + |
1078 | +#include "CopyMoveHandler.h" |
1079 | +#include "RetrieveMetadataHandler.h" |
1080 | +#include "item_id.h" |
1081 | +#include "http_error.h" |
1082 | + |
1083 | +using namespace std; |
1084 | +using namespace unity::storage::provider; |
1085 | + |
1086 | +CopyMoveHandler::CopyMoveHandler(shared_ptr<DavProvider> const& provider, |
1087 | + string const& item_id, |
1088 | + string const& new_parent_id, |
1089 | + string const& new_name, |
1090 | + bool copy, |
1091 | + Context const& ctx) |
1092 | + : provider_(provider), item_id_(item_id), |
1093 | + new_item_id_(make_child_id(new_parent_id, new_name, is_folder(item_id))), |
1094 | + context_(ctx) |
1095 | +{ |
1096 | + QUrl const base_url = provider->base_url(ctx); |
1097 | + QNetworkRequest request(id_to_url(item_id_, base_url)); |
1098 | + request.setRawHeader(QByteArrayLiteral("Destination"), |
1099 | + id_to_url(new_item_id_, base_url).toEncoded()); |
1100 | + // Error out of the operation would overwrite an existing resource. |
1101 | + request.setRawHeader(QByteArrayLiteral("Overwrite"), |
1102 | + QByteArrayLiteral("F")); |
1103 | + |
1104 | + reply_.reset(provider->send_request( |
1105 | + request, copy ? QByteArrayLiteral("COPY") : QByteArrayLiteral("MOVE"), |
1106 | + nullptr, ctx)); |
1107 | + connect(reply_.get(), &QNetworkReply::finished, |
1108 | + this, &CopyMoveHandler::onFinished); |
1109 | +} |
1110 | + |
1111 | +CopyMoveHandler::~CopyMoveHandler() = default; |
1112 | + |
1113 | +boost::future<Item> CopyMoveHandler::get_future() |
1114 | +{ |
1115 | + return promise_.get_future(); |
1116 | +} |
1117 | + |
1118 | +void CopyMoveHandler::onFinished() |
1119 | +{ |
1120 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
1121 | + |
1122 | + if (status != 201 && status != 204) |
1123 | + { |
1124 | + promise_.set_exception( |
1125 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
1126 | + deleteLater(); |
1127 | + return; |
1128 | + } |
1129 | + |
1130 | + metadata_.reset( |
1131 | + new RetrieveMetadataHandler( |
1132 | + provider_, new_item_id_, context_, |
1133 | + [this](Item const& item, boost::exception_ptr const& error) { |
1134 | + if (error) |
1135 | + { |
1136 | + promise_.set_exception(error); |
1137 | + } |
1138 | + else |
1139 | + { |
1140 | + promise_.set_value(item); |
1141 | + } |
1142 | + deleteLater(); |
1143 | + })); |
1144 | +} |
1145 | |
1146 | === added file 'src/CopyMoveHandler.h' |
1147 | --- src/CopyMoveHandler.h 1970-01-01 00:00:00 +0000 |
1148 | +++ src/CopyMoveHandler.h 2016-11-25 05:01:23 +0000 |
1149 | @@ -0,0 +1,57 @@ |
1150 | +/* |
1151 | + * Copyright (C) 2016 Canonical Ltd. |
1152 | + * |
1153 | + * This program is free software: you can redistribute it and/or modify |
1154 | + * it under the terms of the GNU General Public License version 3 as |
1155 | + * published by the Free Software Foundation. |
1156 | + * |
1157 | + * This program is distributed in the hope that it will be useful, |
1158 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1159 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1160 | + * GNU General Public License for more details. |
1161 | + * |
1162 | + * You should have received a copy of the GNU General Public License |
1163 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1164 | + * |
1165 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1166 | + */ |
1167 | + |
1168 | +#pragma once |
1169 | + |
1170 | +#include <QBuffer> |
1171 | +#include <QObject> |
1172 | +#include <QNetworkReply> |
1173 | +#include <unity/storage/provider/ProviderBase.h> |
1174 | + |
1175 | +#include <memory> |
1176 | + |
1177 | +class DavProvider; |
1178 | +class RetrieveMetadataHandler; |
1179 | + |
1180 | +class CopyMoveHandler : public QObject { |
1181 | + Q_OBJECT |
1182 | +public: |
1183 | + CopyMoveHandler(std::shared_ptr<DavProvider> const& provider, |
1184 | + std::string const& item_id, |
1185 | + std::string const& new_parent_id, |
1186 | + std::string const& new_name, |
1187 | + bool copy, |
1188 | + unity::storage::provider::Context const& ctx); |
1189 | + ~CopyMoveHandler(); |
1190 | + |
1191 | + boost::future<unity::storage::provider::Item> get_future(); |
1192 | + |
1193 | +private Q_SLOTS: |
1194 | + void onFinished(); |
1195 | + |
1196 | +private: |
1197 | + boost::promise<unity::storage::provider::Item> promise_; |
1198 | + |
1199 | + std::shared_ptr<DavProvider> const provider_; |
1200 | + std::string const item_id_; |
1201 | + std::string const new_item_id_; |
1202 | + unity::storage::provider::Context const context_; |
1203 | + |
1204 | + std::unique_ptr<QNetworkReply> reply_; |
1205 | + std::unique_ptr<RetrieveMetadataHandler> metadata_; |
1206 | +}; |
1207 | |
1208 | === added file 'src/CreateFolderHandler.cpp' |
1209 | --- src/CreateFolderHandler.cpp 1970-01-01 00:00:00 +0000 |
1210 | +++ src/CreateFolderHandler.cpp 2016-11-25 05:01:23 +0000 |
1211 | @@ -0,0 +1,75 @@ |
1212 | +/* |
1213 | + * Copyright (C) 2016 Canonical Ltd. |
1214 | + * |
1215 | + * This program is free software: you can redistribute it and/or modify |
1216 | + * it under the terms of the GNU General Public License version 3 as |
1217 | + * published by the Free Software Foundation. |
1218 | + * |
1219 | + * This program is distributed in the hope that it will be useful, |
1220 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1221 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1222 | + * GNU General Public License for more details. |
1223 | + * |
1224 | + * You should have received a copy of the GNU General Public License |
1225 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1226 | + * |
1227 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1228 | + */ |
1229 | + |
1230 | +#include "CreateFolderHandler.h" |
1231 | +#include "RetrieveMetadataHandler.h" |
1232 | +#include "item_id.h" |
1233 | +#include "http_error.h" |
1234 | + |
1235 | +using namespace std; |
1236 | +using namespace unity::storage::provider; |
1237 | + |
1238 | +CreateFolderHandler::CreateFolderHandler(shared_ptr<DavProvider> const& provider, |
1239 | + string const& parent_id, |
1240 | + string const& name, |
1241 | + Context const& ctx) |
1242 | + : provider_(provider), item_id_(make_child_id(parent_id, name, true)), |
1243 | + context_(ctx) |
1244 | +{ |
1245 | + QUrl const base_url = provider->base_url(ctx); |
1246 | + QNetworkRequest request(id_to_url(item_id_, base_url)); |
1247 | + reply_.reset(provider->send_request(request, QByteArrayLiteral("MKCOL"), |
1248 | + nullptr, ctx)); |
1249 | + connect(reply_.get(), &QNetworkReply::finished, |
1250 | + this, &CreateFolderHandler::onFinished); |
1251 | +} |
1252 | + |
1253 | +CreateFolderHandler::~CreateFolderHandler() = default; |
1254 | + |
1255 | +boost::future<Item> CreateFolderHandler::get_future() |
1256 | +{ |
1257 | + return promise_.get_future(); |
1258 | +} |
1259 | + |
1260 | +void CreateFolderHandler::onFinished() |
1261 | +{ |
1262 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
1263 | + |
1264 | + if (status != 201) |
1265 | + { |
1266 | + promise_.set_exception( |
1267 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
1268 | + deleteLater(); |
1269 | + return; |
1270 | + } |
1271 | + |
1272 | + metadata_.reset( |
1273 | + new RetrieveMetadataHandler( |
1274 | + provider_, item_id_, context_, |
1275 | + [this](Item const& item, boost::exception_ptr const& error) { |
1276 | + if (error) |
1277 | + { |
1278 | + promise_.set_exception(error); |
1279 | + } |
1280 | + else |
1281 | + { |
1282 | + promise_.set_value(item); |
1283 | + } |
1284 | + deleteLater(); |
1285 | + })); |
1286 | +} |
1287 | |
1288 | === added file 'src/CreateFolderHandler.h' |
1289 | --- src/CreateFolderHandler.h 1970-01-01 00:00:00 +0000 |
1290 | +++ src/CreateFolderHandler.h 2016-11-25 05:01:23 +0000 |
1291 | @@ -0,0 +1,53 @@ |
1292 | +/* |
1293 | + * Copyright (C) 2016 Canonical Ltd. |
1294 | + * |
1295 | + * This program is free software: you can redistribute it and/or modify |
1296 | + * it under the terms of the GNU General Public License version 3 as |
1297 | + * published by the Free Software Foundation. |
1298 | + * |
1299 | + * This program is distributed in the hope that it will be useful, |
1300 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1301 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1302 | + * GNU General Public License for more details. |
1303 | + * |
1304 | + * You should have received a copy of the GNU General Public License |
1305 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1306 | + * |
1307 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1308 | + */ |
1309 | + |
1310 | +#pragma once |
1311 | + |
1312 | +#include <QBuffer> |
1313 | +#include <QObject> |
1314 | +#include <QNetworkReply> |
1315 | +#include <unity/storage/provider/ProviderBase.h> |
1316 | + |
1317 | +#include <memory> |
1318 | + |
1319 | +class DavProvider; |
1320 | +class RetrieveMetadataHandler; |
1321 | + |
1322 | +class CreateFolderHandler : public QObject { |
1323 | + Q_OBJECT |
1324 | +public: |
1325 | + CreateFolderHandler(std::shared_ptr<DavProvider> const& provider, |
1326 | + std::string const& parent_id, std::string const& name, |
1327 | + unity::storage::provider::Context const& ctx); |
1328 | + ~CreateFolderHandler(); |
1329 | + |
1330 | + boost::future<unity::storage::provider::Item> get_future(); |
1331 | + |
1332 | +private Q_SLOTS: |
1333 | + void onFinished(); |
1334 | + |
1335 | +private: |
1336 | + boost::promise<unity::storage::provider::Item> promise_; |
1337 | + |
1338 | + std::shared_ptr<DavProvider> const provider_; |
1339 | + std::string const item_id_; |
1340 | + unity::storage::provider::Context const context_; |
1341 | + |
1342 | + std::unique_ptr<QNetworkReply> reply_; |
1343 | + std::unique_ptr<RetrieveMetadataHandler> metadata_; |
1344 | +}; |
1345 | |
1346 | === added file 'src/DavDownloadJob.cpp' |
1347 | --- src/DavDownloadJob.cpp 1970-01-01 00:00:00 +0000 |
1348 | +++ src/DavDownloadJob.cpp 2016-11-25 05:01:23 +0000 |
1349 | @@ -0,0 +1,215 @@ |
1350 | +/* |
1351 | + * Copyright (C) 2016 Canonical Ltd. |
1352 | + * |
1353 | + * This program is free software: you can redistribute it and/or modify |
1354 | + * it under the terms of the GNU General Public License version 3 as |
1355 | + * published by the Free Software Foundation. |
1356 | + * |
1357 | + * This program is distributed in the hope that it will be useful, |
1358 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1359 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1360 | + * GNU General Public License for more details. |
1361 | + * |
1362 | + * You should have received a copy of the GNU General Public License |
1363 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1364 | + * |
1365 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1366 | + */ |
1367 | + |
1368 | +#include "DavDownloadJob.h" |
1369 | +#include "DavProvider.h" |
1370 | +#include "RetrieveMetadataHandler.h" |
1371 | +#include "item_id.h" |
1372 | +#include "http_error.h" |
1373 | + |
1374 | +#include <unity/storage/provider/Exceptions.h> |
1375 | + |
1376 | +#include <unistd.h> |
1377 | +#include <cassert> |
1378 | +#include <cstdint> |
1379 | +#include <string> |
1380 | + |
1381 | +using namespace std; |
1382 | +using namespace unity::storage::provider; |
1383 | +using unity::storage::ItemType; |
1384 | + |
1385 | +namespace |
1386 | +{ |
1387 | + |
1388 | +string make_download_id() |
1389 | +{ |
1390 | + static int counter = 0; |
1391 | + return to_string(counter++); |
1392 | +} |
1393 | + |
1394 | +constexpr int CHUNK_SIZE = 64 * 1024; |
1395 | + |
1396 | +} |
1397 | + |
1398 | +DavDownloadJob::DavDownloadJob(shared_ptr<DavProvider> const& provider, |
1399 | + string const& item_id, |
1400 | + string const& match_etag, |
1401 | + Context const& ctx) |
1402 | + : QObject(), DownloadJob(make_download_id()), provider_(provider), |
1403 | + item_id_(item_id) |
1404 | +{ |
1405 | + QUrl base_url = provider->base_url(ctx); |
1406 | + QNetworkRequest request(id_to_url(item_id, base_url)); |
1407 | + if (!match_etag.empty()) |
1408 | + { |
1409 | + request.setRawHeader(QByteArrayLiteral("If-Match"), |
1410 | + QByteArray::fromStdString(match_etag)); |
1411 | + } |
1412 | + |
1413 | + reply_.reset(provider->send_request( |
1414 | + request, QByteArrayLiteral("GET"), nullptr, ctx)); |
1415 | + assert(reply_.get() != nullptr); |
1416 | + reply_->setReadBufferSize(CHUNK_SIZE); |
1417 | + connect(reply_.get(), &QNetworkReply::finished, |
1418 | + this, &DavDownloadJob::onReplyFinished); |
1419 | + connect(reply_.get(), &QIODevice::readyRead, |
1420 | + this, &DavDownloadJob::onReplyReadyRead); |
1421 | + connect(reply_.get(), &QIODevice::readChannelFinished, |
1422 | + this, &DavDownloadJob::onReplyReadChannelFinished); |
1423 | + writer_.setSocketDescriptor( |
1424 | + dup(write_socket()), QLocalSocket::ConnectedState, QIODevice::WriteOnly); |
1425 | + connect(&writer_, &QIODevice::bytesWritten, |
1426 | + this, &DavDownloadJob::onSocketBytesWritten); |
1427 | +} |
1428 | + |
1429 | +DavDownloadJob::~DavDownloadJob() = default; |
1430 | + |
1431 | +void DavDownloadJob::onReplyFinished() |
1432 | +{ |
1433 | + if (!seen_header_ || is_error_) |
1434 | + { |
1435 | + try |
1436 | + { |
1437 | + boost::rethrow_exception( |
1438 | + translate_http_error(reply_.get(), error_body_, item_id_)); |
1439 | + } |
1440 | + catch (...) |
1441 | + { |
1442 | + handle_error(std::current_exception()); |
1443 | + } |
1444 | + } |
1445 | +} |
1446 | + |
1447 | +void DavDownloadJob::onReplyReadyRead() |
1448 | +{ |
1449 | + if (!seen_header_) |
1450 | + { |
1451 | + seen_header_ = true; |
1452 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
1453 | + if (status != 200) |
1454 | + { |
1455 | + is_error_ = true; |
1456 | + } |
1457 | + } |
1458 | + |
1459 | + if (is_error_) |
1460 | + { |
1461 | + if (error_body_.size() < MAX_ERROR_BODY_LENGTH) |
1462 | + { |
1463 | + error_body_.append(reply_->readAll()); |
1464 | + } |
1465 | + else |
1466 | + { |
1467 | + reply_->close(); |
1468 | + } |
1469 | + } |
1470 | + else |
1471 | + { |
1472 | + maybe_send_chunk(); |
1473 | + } |
1474 | +} |
1475 | + |
1476 | +void DavDownloadJob::onReplyReadChannelFinished() |
1477 | +{ |
1478 | + if (is_error_) |
1479 | + { |
1480 | + return; |
1481 | + } |
1482 | + |
1483 | + read_channel_finished_ = true; |
1484 | + maybe_send_chunk(); |
1485 | +} |
1486 | + |
1487 | +void DavDownloadJob::onSocketBytesWritten(int64_t bytes) |
1488 | +{ |
1489 | + if (is_error_) |
1490 | + { |
1491 | + return; |
1492 | + } |
1493 | + |
1494 | + bytes_written_ += bytes; |
1495 | + maybe_send_chunk(); |
1496 | +} |
1497 | + |
1498 | +void DavDownloadJob::maybe_send_chunk() |
1499 | +{ |
1500 | + assert(bytes_written_ <= bytes_read_); |
1501 | + // If there are outstanding writes, do nothing. |
1502 | + if (bytes_written_ < bytes_read_) |
1503 | + { |
1504 | + return; |
1505 | + } |
1506 | + // If there are no bytes available, return. |
1507 | + if (reply_->bytesAvailable() == 0) |
1508 | + { |
1509 | + // If we've reached the end of the input, and all data has been |
1510 | + // written out, signal completion. |
1511 | + if (read_channel_finished_ && bytes_written_ == bytes_read_) |
1512 | + { |
1513 | + writer_.close(); |
1514 | + report_complete(); |
1515 | + } |
1516 | + return; |
1517 | + } |
1518 | + |
1519 | + char buffer[CHUNK_SIZE]; |
1520 | + int n_read = reply_->read(buffer, CHUNK_SIZE); |
1521 | + if (n_read < 0) |
1522 | + { |
1523 | + handle_error(RemoteCommsException("Failed to read from server: " + |
1524 | + reply_->errorString().toStdString())); |
1525 | + return; |
1526 | + } |
1527 | + bytes_read_ += n_read; |
1528 | + |
1529 | + int n_written = writer_.write(buffer, n_read); |
1530 | + if (n_written < 0) |
1531 | + { |
1532 | + handle_error(ResourceException( |
1533 | + "Error writing to socket: " |
1534 | + + writer_.errorString().toStdString(), 0)); |
1535 | + return; |
1536 | + } |
1537 | +} |
1538 | + |
1539 | +void DavDownloadJob::handle_error(StorageException const& exc) |
1540 | +{ |
1541 | + handle_error(std::make_exception_ptr(exc)); |
1542 | +} |
1543 | + |
1544 | +void DavDownloadJob::handle_error(std::exception_ptr ep) |
1545 | +{ |
1546 | + is_error_ = true; |
1547 | + reply_->close(); |
1548 | + writer_.close(); |
1549 | + report_error(ep); |
1550 | +} |
1551 | + |
1552 | + |
1553 | +boost::future<void> DavDownloadJob::cancel() |
1554 | +{ |
1555 | + reply_->abort(); |
1556 | + writer_.close(); |
1557 | + return boost::make_ready_future(); |
1558 | +} |
1559 | + |
1560 | +boost::future<void> DavDownloadJob::finish() |
1561 | +{ |
1562 | + return boost::make_exceptional_future<void>( |
1563 | + LogicException("finish called before all data sent")); |
1564 | +} |
1565 | |
1566 | === added file 'src/DavDownloadJob.h' |
1567 | --- src/DavDownloadJob.h 1970-01-01 00:00:00 +0000 |
1568 | +++ src/DavDownloadJob.h 2016-11-25 05:01:23 +0000 |
1569 | @@ -0,0 +1,72 @@ |
1570 | +/* |
1571 | + * Copyright (C) 2016 Canonical Ltd. |
1572 | + * |
1573 | + * This program is free software: you can redistribute it and/or modify |
1574 | + * it under the terms of the GNU General Public License version 3 as |
1575 | + * published by the Free Software Foundation. |
1576 | + * |
1577 | + * This program is distributed in the hope that it will be useful, |
1578 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1579 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1580 | + * GNU General Public License for more details. |
1581 | + * |
1582 | + * You should have received a copy of the GNU General Public License |
1583 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1584 | + * |
1585 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1586 | + */ |
1587 | + |
1588 | +#pragma once |
1589 | + |
1590 | +#include <QByteArray> |
1591 | +#include <QLocalSocket> |
1592 | +#include <QNetworkReply> |
1593 | +#include <QObject> |
1594 | +#include <QUrl> |
1595 | +#include <unity/storage/provider/Exceptions.h> |
1596 | +#include <unity/storage/provider/ProviderBase.h> |
1597 | +#include <unity/storage/provider/DownloadJob.h> |
1598 | + |
1599 | +#include <cstdint> |
1600 | +#include <memory> |
1601 | +#include <string> |
1602 | + |
1603 | +class DavProvider; |
1604 | + |
1605 | +class DavDownloadJob : public QObject, public unity::storage::provider::DownloadJob |
1606 | +{ |
1607 | + Q_OBJECT |
1608 | +public: |
1609 | + DavDownloadJob(std::shared_ptr<DavProvider> const& provider, |
1610 | + std::string const& item_id, std::string const& match_etag, |
1611 | + unity::storage::provider::Context const& ctx); |
1612 | + ~DavDownloadJob(); |
1613 | + |
1614 | + boost::future<void> cancel() override; |
1615 | + boost::future<void> finish() override; |
1616 | + |
1617 | +private Q_SLOTS: |
1618 | + void onReplyFinished(); |
1619 | + void onReplyReadyRead(); |
1620 | + void onReplyReadChannelFinished(); |
1621 | + |
1622 | + void onSocketBytesWritten(int64_t bytes); |
1623 | + |
1624 | +private: |
1625 | + void maybe_send_chunk(); |
1626 | + void handle_error(unity::storage::provider::StorageException const& exc); |
1627 | + void handle_error(std::exception_ptr ep); |
1628 | + |
1629 | + std::shared_ptr<DavProvider> const provider_; |
1630 | + std::string const item_id_; |
1631 | + QLocalSocket writer_; |
1632 | + std::unique_ptr<QNetworkReply> reply_; |
1633 | + |
1634 | + bool seen_header_ = false; |
1635 | + bool read_channel_finished_ = false; |
1636 | + int64_t bytes_read_ = 0; |
1637 | + int64_t bytes_written_ = 0; |
1638 | + |
1639 | + bool is_error_ = false; |
1640 | + QByteArray error_body_; |
1641 | +}; |
1642 | |
1643 | === modified file 'src/DavProvider.cpp' |
1644 | --- src/DavProvider.cpp 2016-09-22 06:40:07 +0000 |
1645 | +++ src/DavProvider.cpp 2016-11-25 05:01:23 +0000 |
1646 | @@ -1,12 +1,46 @@ |
1647 | +/* |
1648 | + * Copyright (C) 2016 Canonical Ltd. |
1649 | + * |
1650 | + * This program is free software: you can redistribute it and/or modify |
1651 | + * it under the terms of the GNU General Public License version 3 as |
1652 | + * published by the Free Software Foundation. |
1653 | + * |
1654 | + * This program is distributed in the hope that it will be useful, |
1655 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1656 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1657 | + * GNU General Public License for more details. |
1658 | + * |
1659 | + * You should have received a copy of the GNU General Public License |
1660 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1661 | + * |
1662 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1663 | + */ |
1664 | + |
1665 | #include "DavProvider.h" |
1666 | +#include "MultiStatusParser.h" |
1667 | #include "RootsHandler.h" |
1668 | +#include "ListHandler.h" |
1669 | +#include "LookupHandler.h" |
1670 | +#include "MetadataHandler.h" |
1671 | +#include "DavDownloadJob.h" |
1672 | +#include "DavUploadJob.h" |
1673 | +#include "CreateFolderHandler.h" |
1674 | +#include "DeleteHandler.h" |
1675 | +#include "CopyMoveHandler.h" |
1676 | +#include "item_id.h" |
1677 | |
1678 | +#include <QDateTime> |
1679 | +#include <QDebug> |
1680 | #include <QNetworkAccessManager> |
1681 | #include <QNetworkReply> |
1682 | #include <QNetworkRequest> |
1683 | +#include <unity/storage/common.h> |
1684 | +#include <unity/storage/provider/Exceptions.h> |
1685 | |
1686 | using namespace std; |
1687 | using namespace unity::storage::provider; |
1688 | +using namespace unity::storage::metadata; |
1689 | +using unity::storage::ItemType; |
1690 | |
1691 | DavProvider::DavProvider() |
1692 | : network_(new QNetworkAccessManager) |
1693 | @@ -15,62 +49,198 @@ |
1694 | |
1695 | DavProvider::~DavProvider() = default; |
1696 | |
1697 | -boost::future<ItemList> DavProvider::roots(Context const& ctx) |
1698 | -{ |
1699 | - auto handler = new RootsHandler(*this, ctx); |
1700 | +std::shared_ptr<DavProvider> DavProvider::shared_from_this() |
1701 | +{ |
1702 | + return static_pointer_cast<DavProvider>(ProviderBase::shared_from_this()); |
1703 | +} |
1704 | + |
1705 | +boost::future<ItemList> DavProvider::roots( |
1706 | + vector<string> const& metadata_keys, Context const& ctx) |
1707 | +{ |
1708 | + Q_UNUSED(metadata_keys); |
1709 | + auto handler = new RootsHandler(shared_from_this(), ctx); |
1710 | return handler->get_future(); |
1711 | } |
1712 | |
1713 | boost::future<tuple<ItemList,string>> DavProvider::list( |
1714 | - string const& item_id, string const& page_token, Context const& ctx) |
1715 | + string const& item_id, string const& page_token, |
1716 | + vector<string> const& metadata_keys, Context const& ctx) |
1717 | { |
1718 | + Q_UNUSED(metadata_keys); |
1719 | + if (!page_token.empty()) |
1720 | + { |
1721 | + throw InvalidArgumentException("Invalid paging token: " + page_token); |
1722 | + } |
1723 | + auto handler = new ListHandler(shared_from_this(), item_id, ctx); |
1724 | + return handler->get_future(); |
1725 | } |
1726 | |
1727 | boost::future<ItemList> DavProvider::lookup( |
1728 | - string const& parent_id, string const& name, Context const& ctx) |
1729 | + string const& parent_id, string const& name, |
1730 | + vector<string> const& metadata_keys, Context const& ctx) |
1731 | { |
1732 | + Q_UNUSED(metadata_keys); |
1733 | + string item_id = make_child_id(parent_id, name); |
1734 | + auto handler = new LookupHandler(shared_from_this(), item_id, ctx); |
1735 | + return handler->get_future(); |
1736 | } |
1737 | |
1738 | boost::future<Item> DavProvider::metadata( |
1739 | - string const& item_id, Context const& ctx) |
1740 | + string const& item_id, vector<string> const& metadata_keys, |
1741 | + Context const& ctx) |
1742 | { |
1743 | + Q_UNUSED(metadata_keys); |
1744 | + auto handler = new MetadataHandler(shared_from_this(), item_id, ctx); |
1745 | + return handler->get_future(); |
1746 | } |
1747 | |
1748 | boost::future<Item> DavProvider::create_folder( |
1749 | - string const& parent_id, string const& name, Context const& ctx) |
1750 | + string const& parent_id, string const& name, |
1751 | + vector<string> const& metadata_keys, Context const& ctx) |
1752 | { |
1753 | + Q_UNUSED(metadata_keys); |
1754 | + auto handler = new CreateFolderHandler( |
1755 | + shared_from_this(), parent_id, name, ctx); |
1756 | + return handler->get_future(); |
1757 | } |
1758 | |
1759 | boost::future<unique_ptr<UploadJob>> DavProvider::create_file( |
1760 | string const& parent_id, string const& name, int64_t size, |
1761 | - string const& content_type, bool allow_overwrite, Context const& ctx) |
1762 | + string const& content_type, bool allow_overwrite, |
1763 | + vector<string> const& metadata_keys, Context const& ctx) |
1764 | { |
1765 | + Q_UNUSED(metadata_keys); |
1766 | + string item_id = make_child_id(parent_id, name); |
1767 | + boost::promise<unique_ptr<UploadJob>> p; |
1768 | + p.set_value(unique_ptr<UploadJob>(new DavUploadJob( |
1769 | + shared_from_this(), item_id, size, content_type, allow_overwrite, |
1770 | + string(), ctx))); |
1771 | + return p.get_future(); |
1772 | } |
1773 | |
1774 | boost::future<unique_ptr<UploadJob>> DavProvider::update( |
1775 | string const& item_id, int64_t size, string const& old_etag, |
1776 | - Context const& ctx) |
1777 | + vector<string> const& metadata_keys, Context const& ctx) |
1778 | { |
1779 | + Q_UNUSED(metadata_keys); |
1780 | + boost::promise<unique_ptr<UploadJob>> p; |
1781 | + p.set_value(unique_ptr<UploadJob>(new DavUploadJob( |
1782 | + shared_from_this(), item_id, size, string(), true, old_etag, ctx))); |
1783 | + return p.get_future(); |
1784 | } |
1785 | |
1786 | boost::future<unique_ptr<DownloadJob>> DavProvider::download( |
1787 | - string const& item_id, Context const& ctx) |
1788 | + string const& item_id, string const& match_etag, Context const& ctx) |
1789 | { |
1790 | + boost::promise<unique_ptr<DownloadJob>> p; |
1791 | + p.set_value(unique_ptr<DownloadJob>(new DavDownloadJob( |
1792 | + shared_from_this(), item_id, match_etag, ctx))); |
1793 | + return p.get_future(); |
1794 | } |
1795 | |
1796 | boost::future<void> DavProvider::delete_item( |
1797 | string const& item_id, Context const& ctx) |
1798 | { |
1799 | + auto handler = new DeleteHandler(shared_from_this(), item_id, ctx); |
1800 | + return handler->get_future(); |
1801 | } |
1802 | |
1803 | boost::future<Item> DavProvider::move( |
1804 | string const& item_id, string const& new_parent_id, string const& new_name, |
1805 | - Context const& ctx) |
1806 | + vector<string> const& metadata_keys, Context const& ctx) |
1807 | { |
1808 | + Q_UNUSED(metadata_keys); |
1809 | + auto handler = new CopyMoveHandler( |
1810 | + shared_from_this(), item_id, new_parent_id, new_name, false, ctx); |
1811 | + return handler->get_future(); |
1812 | } |
1813 | |
1814 | boost::future<Item> DavProvider::copy( |
1815 | string const& item_id, string const& new_parent_id, string const& new_name, |
1816 | - Context const& ctx) |
1817 | -{ |
1818 | + vector<string> const& metadata_keys, Context const& ctx) |
1819 | +{ |
1820 | + Q_UNUSED(metadata_keys); |
1821 | + auto handler = new CopyMoveHandler( |
1822 | + shared_from_this(), item_id, new_parent_id, new_name, true, ctx); |
1823 | + return handler->get_future(); |
1824 | +} |
1825 | + |
1826 | +Item DavProvider::make_item(QUrl const& href, QUrl const& base_url, |
1827 | + vector<MultiStatusProperty> const& properties) const |
1828 | +{ |
1829 | + Item item; |
1830 | + item.item_id = url_to_id(href, base_url); |
1831 | + |
1832 | + QByteArray path = href.path(QUrl::FullyEncoded | |
1833 | + QUrl::StripTrailingSlash).toUtf8(); |
1834 | + int pos = path.lastIndexOf('/'); |
1835 | + assert(pos >= 0); |
1836 | + try |
1837 | + { |
1838 | + item.parent_ids.emplace_back(url_to_id( |
1839 | + href.resolved(QUrl::fromEncoded(path.mid(0, pos+1), |
1840 | + QUrl::StrictMode)), base_url)); |
1841 | + } |
1842 | + catch (RemoteCommsException const&) |
1843 | + { |
1844 | + // Path is outside of base URL: don't expose. |
1845 | + } |
1846 | + |
1847 | + item.name = QUrl::fromPercentEncoding(path.mid(pos+1)).toStdString(); |
1848 | + item.type = ItemType::file; |
1849 | + |
1850 | + for (const auto& prop : properties) |
1851 | + { |
1852 | + if (prop.status != 200) |
1853 | + { |
1854 | + // Don't warn about "404 Not Found" properties |
1855 | + if (prop.status != 404) |
1856 | + { |
1857 | + qWarning() << "Got status" << prop.status << "for property" |
1858 | + << prop.ns << prop.name; |
1859 | + } |
1860 | + continue; |
1861 | + } |
1862 | + if (prop.ns == "DAV:") |
1863 | + { |
1864 | + if (prop.name == "resourcetype") |
1865 | + { |
1866 | + if (prop.value == "DAV:collection") { |
1867 | + item.type = ItemType::folder; |
1868 | + } |
1869 | + } |
1870 | + if (prop.name == "getetag") |
1871 | + { |
1872 | + item.etag = prop.value.toStdString(); |
1873 | + } |
1874 | + else if (prop.name == "getcontentlength") |
1875 | + { |
1876 | + item.metadata[SIZE_IN_BYTES] = static_cast<int64_t>(prop.value.toLongLong()); |
1877 | + } |
1878 | + else if (prop.name == "creationdate") |
1879 | + { |
1880 | + auto date = QDateTime::fromString(prop.value, Qt::RFC2822Date); |
1881 | + if (date.isValid()) |
1882 | + { |
1883 | + item.metadata[CREATION_TIME] = date.toString(Qt::ISODate).toStdString(); |
1884 | + } |
1885 | + } |
1886 | + else if (prop.name == "getlastmodified") |
1887 | + { |
1888 | + auto date = QDateTime::fromString(prop.value, Qt::RFC2822Date); |
1889 | + if (date.isValid()) |
1890 | + { |
1891 | + item.metadata[LAST_MODIFIED_TIME] = date.toString(Qt::ISODate).toStdString(); |
1892 | + } |
1893 | + } |
1894 | + } |
1895 | + } |
1896 | + |
1897 | + if (href == base_url) |
1898 | + { |
1899 | + item.type = ItemType::root; |
1900 | + item.name = "Root"; |
1901 | + item.parent_ids.clear(); |
1902 | + } |
1903 | + return item; |
1904 | } |
1905 | |
1906 | === modified file 'src/DavProvider.h' |
1907 | --- src/DavProvider.h 2016-09-22 03:36:22 +0000 |
1908 | +++ src/DavProvider.h 2016-11-25 05:01:23 +0000 |
1909 | @@ -1,3 +1,21 @@ |
1910 | +/* |
1911 | + * Copyright (C) 2016 Canonical Ltd. |
1912 | + * |
1913 | + * This program is free software: you can redistribute it and/or modify |
1914 | + * it under the terms of the GNU General Public License version 3 as |
1915 | + * published by the Free Software Foundation. |
1916 | + * |
1917 | + * This program is distributed in the hope that it will be useful, |
1918 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1919 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1920 | + * GNU General Public License for more details. |
1921 | + * |
1922 | + * You should have received a copy of the GNU General Public License |
1923 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1924 | + * |
1925 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
1926 | + */ |
1927 | + |
1928 | #pragma once |
1929 | |
1930 | #include <unity/storage/provider/ProviderBase.h> |
1931 | @@ -10,6 +28,7 @@ |
1932 | class QNetworkReply; |
1933 | class QNetworkRequest; |
1934 | class QUrl; |
1935 | +struct MultiStatusProperty; |
1936 | |
1937 | class DavProvider : public unity::storage::provider::ProviderBase |
1938 | { |
1939 | @@ -18,31 +37,39 @@ |
1940 | virtual ~DavProvider(); |
1941 | |
1942 | boost::future<unity::storage::provider::ItemList> roots( |
1943 | + std::vector<std::string> const& metadata_keys, |
1944 | unity::storage::provider::Context const& ctx) override; |
1945 | boost::future<std::tuple<unity::storage::provider::ItemList,std::string>> list( |
1946 | std::string const& item_id, std::string const& page_token, |
1947 | + std::vector<std::string> const& metadata_keys, |
1948 | unity::storage::provider::Context const& ctx) override; |
1949 | boost::future<unity::storage::provider::ItemList> lookup( |
1950 | std::string const& parent_id, std::string const& name, |
1951 | + std::vector<std::string> const& metadata_keys, |
1952 | unity::storage::provider::Context const& ctx) override; |
1953 | boost::future<unity::storage::provider::Item> metadata( |
1954 | std::string const& item_id, |
1955 | + std::vector<std::string> const& metadata_keys, |
1956 | unity::storage::provider::Context const& ctx) override; |
1957 | |
1958 | boost::future<unity::storage::provider::Item> create_folder( |
1959 | std::string const& parent_id, std::string const& name, |
1960 | + std::vector<std::string> const& metadata_keys, |
1961 | unity::storage::provider::Context const& ctx) override; |
1962 | boost::future<std::unique_ptr<unity::storage::provider::UploadJob>> create_file( |
1963 | std::string const& parent_id, std::string const& name, |
1964 | int64_t size, std::string const& content_type, bool allow_overwrite, |
1965 | + std::vector<std::string> const& metadata_keys, |
1966 | unity::storage::provider::Context const& ctx) override; |
1967 | boost::future<std::unique_ptr<unity::storage::provider::UploadJob>> update( |
1968 | std::string const& item_id, int64_t size, |
1969 | std::string const& old_etag, |
1970 | + std::vector<std::string> const& metadata_keys, |
1971 | unity::storage::provider::Context const& ctx) override; |
1972 | |
1973 | boost::future<std::unique_ptr<unity::storage::provider::DownloadJob>> download( |
1974 | std::string const& item_id, |
1975 | + std::string const& match_etag, |
1976 | unity::storage::provider::Context const& ctx) override; |
1977 | |
1978 | boost::future<void> delete_item(std::string const& item_id, |
1979 | @@ -50,10 +77,12 @@ |
1980 | boost::future<unity::storage::provider::Item> move( |
1981 | std::string const& item_id, std::string const& new_parent_id, |
1982 | std::string const& new_name, |
1983 | + std::vector<std::string> const& metadata_keys, |
1984 | unity::storage::provider::Context const& ctx) override; |
1985 | boost::future<unity::storage::provider::Item> copy( |
1986 | std::string const& item_id, std::string const& new_parent_id, |
1987 | std::string const& new_name, |
1988 | + std::vector<std::string> const& metadata_keys, |
1989 | unity::storage::provider::Context const& ctx) override; |
1990 | |
1991 | virtual QUrl base_url( |
1992 | @@ -61,7 +90,13 @@ |
1993 | virtual QNetworkReply *send_request( |
1994 | QNetworkRequest& request, QByteArray const& verb, QIODevice* data, |
1995 | unity::storage::provider::Context const& ctx) const = 0; |
1996 | + virtual unity::storage::provider::Item make_item( |
1997 | + QUrl const& href, QUrl const& base_url, |
1998 | + std::vector<MultiStatusProperty> const& properties) const; |
1999 | |
2000 | protected: |
2001 | std::unique_ptr<QNetworkAccessManager> const network_; |
2002 | + |
2003 | +private: |
2004 | + inline std::shared_ptr<DavProvider> shared_from_this(); |
2005 | }; |
2006 | |
2007 | === added file 'src/DavUploadJob.cpp' |
2008 | --- src/DavUploadJob.cpp 1970-01-01 00:00:00 +0000 |
2009 | +++ src/DavUploadJob.cpp 2016-11-25 05:01:23 +0000 |
2010 | @@ -0,0 +1,140 @@ |
2011 | +/* |
2012 | + * Copyright (C) 2016 Canonical Ltd. |
2013 | + * |
2014 | + * This program is free software: you can redistribute it and/or modify |
2015 | + * it under the terms of the GNU General Public License version 3 as |
2016 | + * published by the Free Software Foundation. |
2017 | + * |
2018 | + * This program is distributed in the hope that it will be useful, |
2019 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2020 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2021 | + * GNU General Public License for more details. |
2022 | + * |
2023 | + * You should have received a copy of the GNU General Public License |
2024 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2025 | + * |
2026 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2027 | + */ |
2028 | + |
2029 | +#include "DavUploadJob.h" |
2030 | +#include "DavProvider.h" |
2031 | +#include "RetrieveMetadataHandler.h" |
2032 | +#include "item_id.h" |
2033 | +#include "http_error.h" |
2034 | + |
2035 | +#include <unity/storage/provider/Exceptions.h> |
2036 | + |
2037 | +#include <unistd.h> |
2038 | +#include <cassert> |
2039 | +#include <cstdint> |
2040 | +#include <string> |
2041 | + |
2042 | +using namespace std; |
2043 | +using namespace unity::storage::provider; |
2044 | +using unity::storage::ItemType; |
2045 | + |
2046 | +namespace |
2047 | +{ |
2048 | + |
2049 | +string make_upload_id() |
2050 | +{ |
2051 | + static int counter = 0; |
2052 | + return to_string(counter++); |
2053 | +} |
2054 | + |
2055 | +} |
2056 | + |
2057 | +DavUploadJob::DavUploadJob(shared_ptr<DavProvider> const& provider, |
2058 | + string const& item_id, int64_t size, |
2059 | + string const& content_type, bool allow_overwrite, |
2060 | + string const& old_etag, Context const& ctx) |
2061 | + : QObject(), UploadJob(make_upload_id()), provider_(provider), |
2062 | + item_id_(item_id), base_url_(provider->base_url(ctx)), size_(size), |
2063 | + context_(ctx) |
2064 | +{ |
2065 | + QNetworkRequest request(id_to_url(item_id, base_url_)); |
2066 | + if (!content_type.empty()) |
2067 | + { |
2068 | + request.setHeader(QNetworkRequest::ContentTypeHeader, |
2069 | + QByteArray::fromStdString(content_type)); |
2070 | + } |
2071 | + if (!allow_overwrite) |
2072 | + { |
2073 | + request.setRawHeader(QByteArrayLiteral("If-None-Match"), |
2074 | + QByteArrayLiteral("*")); |
2075 | + } |
2076 | + if (!old_etag.empty()) |
2077 | + { |
2078 | + request.setRawHeader(QByteArrayLiteral("If-Match"), |
2079 | + QByteArray::fromStdString(old_etag)); |
2080 | + } |
2081 | + request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(size)); |
2082 | + request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, true); |
2083 | + |
2084 | + reader_.setSocketDescriptor( |
2085 | + dup(read_socket()), QLocalSocket::ConnectedState, QIODevice::ReadOnly); |
2086 | + reply_.reset(provider->send_request( |
2087 | + request, QByteArrayLiteral("PUT"), &reader_, ctx)); |
2088 | + assert(reply_.get() != nullptr); |
2089 | + connect(reply_.get(), &QNetworkReply::finished, |
2090 | + this, &DavUploadJob::onReplyFinished); |
2091 | +} |
2092 | + |
2093 | +DavUploadJob::~DavUploadJob() = default; |
2094 | + |
2095 | +void DavUploadJob::onReplyFinished() |
2096 | +{ |
2097 | + if (promise_set_) |
2098 | + { |
2099 | + return; |
2100 | + } |
2101 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
2102 | + // Is this a success status code? |
2103 | + if (status / 100 != 2) |
2104 | + { |
2105 | + promise_.set_exception( |
2106 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
2107 | + promise_set_ = true; |
2108 | + return; |
2109 | + } |
2110 | + // Queue up a PROPFIND request to retrieve the metadata for the upload. |
2111 | + metadata_.reset( |
2112 | + new RetrieveMetadataHandler( |
2113 | + provider_, item_id_, context_, |
2114 | + [this](Item const& item, boost::exception_ptr const& error) { |
2115 | + if (promise_set_) |
2116 | + { |
2117 | + return; |
2118 | + } |
2119 | + if (error) |
2120 | + { |
2121 | + promise_.set_exception(error); |
2122 | + } |
2123 | + else |
2124 | + { |
2125 | + promise_.set_value(item); |
2126 | + } |
2127 | + promise_set_ = true; |
2128 | + })); |
2129 | +} |
2130 | + |
2131 | +boost::future<void> DavUploadJob::cancel() |
2132 | +{ |
2133 | + if (!promise_set_) |
2134 | + { |
2135 | + if (metadata_) |
2136 | + { |
2137 | + metadata_->abort(); |
2138 | + } |
2139 | + else |
2140 | + { |
2141 | + reply_->abort(); |
2142 | + } |
2143 | + } |
2144 | + return boost::make_ready_future(); |
2145 | +} |
2146 | + |
2147 | +boost::future<Item> DavUploadJob::finish() |
2148 | +{ |
2149 | + return promise_.get_future(); |
2150 | +} |
2151 | |
2152 | === added file 'src/DavUploadJob.h' |
2153 | --- src/DavUploadJob.h 1970-01-01 00:00:00 +0000 |
2154 | +++ src/DavUploadJob.h 2016-11-25 05:01:23 +0000 |
2155 | @@ -0,0 +1,65 @@ |
2156 | +/* |
2157 | + * Copyright (C) 2016 Canonical Ltd. |
2158 | + * |
2159 | + * This program is free software: you can redistribute it and/or modify |
2160 | + * it under the terms of the GNU General Public License version 3 as |
2161 | + * published by the Free Software Foundation. |
2162 | + * |
2163 | + * This program is distributed in the hope that it will be useful, |
2164 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2165 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2166 | + * GNU General Public License for more details. |
2167 | + * |
2168 | + * You should have received a copy of the GNU General Public License |
2169 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2170 | + * |
2171 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2172 | + */ |
2173 | + |
2174 | +#pragma once |
2175 | + |
2176 | +#include <QLocalSocket> |
2177 | +#include <QNetworkReply> |
2178 | +#include <QObject> |
2179 | +#include <QUrl> |
2180 | +#include <unity/storage/provider/ProviderBase.h> |
2181 | +#include <unity/storage/provider/UploadJob.h> |
2182 | + |
2183 | +#include <cstdint> |
2184 | +#include <memory> |
2185 | +#include <string> |
2186 | + |
2187 | +class DavProvider; |
2188 | +class RetrieveMetadataHandler; |
2189 | + |
2190 | +class DavUploadJob : public QObject, public unity::storage::provider::UploadJob |
2191 | +{ |
2192 | + Q_OBJECT |
2193 | +public: |
2194 | + DavUploadJob(std::shared_ptr<DavProvider> const& provider, |
2195 | + std::string const& item_id, int64_t size, |
2196 | + std::string const& content_type, bool allow_overwrite, |
2197 | + std::string const& old_etag, |
2198 | + unity::storage::provider::Context const& ctx); |
2199 | + ~DavUploadJob(); |
2200 | + |
2201 | + boost::future<void> cancel() override; |
2202 | + boost::future<unity::storage::provider::Item> finish() override; |
2203 | + |
2204 | +private Q_SLOTS: |
2205 | + void onReplyFinished(); |
2206 | + |
2207 | +private: |
2208 | + std::shared_ptr<DavProvider> const provider_; |
2209 | + std::string const item_id_; |
2210 | + QUrl const base_url_; |
2211 | + int64_t const size_; |
2212 | + unity::storage::provider::Context const context_; |
2213 | + QLocalSocket reader_; |
2214 | + std::unique_ptr<QNetworkReply> reply_; |
2215 | + |
2216 | + std::unique_ptr<RetrieveMetadataHandler> metadata_; |
2217 | + |
2218 | + bool promise_set_ = false; |
2219 | + boost::promise<unity::storage::provider::Item> promise_; |
2220 | +}; |
2221 | |
2222 | === added file 'src/DeleteHandler.cpp' |
2223 | --- src/DeleteHandler.cpp 1970-01-01 00:00:00 +0000 |
2224 | +++ src/DeleteHandler.cpp 2016-11-25 05:01:23 +0000 |
2225 | @@ -0,0 +1,62 @@ |
2226 | +/* |
2227 | + * Copyright (C) 2016 Canonical Ltd. |
2228 | + * |
2229 | + * This program is free software: you can redistribute it and/or modify |
2230 | + * it under the terms of the GNU General Public License version 3 as |
2231 | + * published by the Free Software Foundation. |
2232 | + * |
2233 | + * This program is distributed in the hope that it will be useful, |
2234 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2235 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2236 | + * GNU General Public License for more details. |
2237 | + * |
2238 | + * You should have received a copy of the GNU General Public License |
2239 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2240 | + * |
2241 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2242 | + */ |
2243 | + |
2244 | +#include "DeleteHandler.h" |
2245 | +#include "DavProvider.h" |
2246 | +#include "item_id.h" |
2247 | +#include "http_error.h" |
2248 | + |
2249 | +using namespace std; |
2250 | +using namespace unity::storage::provider; |
2251 | + |
2252 | +DeleteHandler::DeleteHandler(shared_ptr<DavProvider> const& provider, |
2253 | + string const& item_id, |
2254 | + Context const& ctx) |
2255 | + : provider_(provider), item_id_(item_id) |
2256 | +{ |
2257 | + QUrl const base_url = provider->base_url(ctx); |
2258 | + QNetworkRequest request(id_to_url(item_id_, base_url)); |
2259 | + reply_.reset(provider->send_request(request, QByteArrayLiteral("DELETE"), |
2260 | + nullptr, ctx)); |
2261 | + connect(reply_.get(), &QNetworkReply::finished, |
2262 | + this, &DeleteHandler::onFinished); |
2263 | +} |
2264 | + |
2265 | +DeleteHandler::~DeleteHandler() = default; |
2266 | + |
2267 | +boost::future<void> DeleteHandler::get_future() |
2268 | +{ |
2269 | + return promise_.get_future(); |
2270 | +} |
2271 | + |
2272 | +void DeleteHandler::onFinished() |
2273 | +{ |
2274 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
2275 | + |
2276 | + if (status / 100 == 2) |
2277 | + { |
2278 | + promise_.set_value(); |
2279 | + } |
2280 | + else |
2281 | + { |
2282 | + promise_.set_exception( |
2283 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
2284 | + } |
2285 | + |
2286 | + deleteLater(); |
2287 | +} |
2288 | |
2289 | === added file 'src/DeleteHandler.h' |
2290 | --- src/DeleteHandler.h 1970-01-01 00:00:00 +0000 |
2291 | +++ src/DeleteHandler.h 2016-11-25 05:01:23 +0000 |
2292 | @@ -0,0 +1,49 @@ |
2293 | +/* |
2294 | + * Copyright (C) 2016 Canonical Ltd. |
2295 | + * |
2296 | + * This program is free software: you can redistribute it and/or modify |
2297 | + * it under the terms of the GNU General Public License version 3 as |
2298 | + * published by the Free Software Foundation. |
2299 | + * |
2300 | + * This program is distributed in the hope that it will be useful, |
2301 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2302 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2303 | + * GNU General Public License for more details. |
2304 | + * |
2305 | + * You should have received a copy of the GNU General Public License |
2306 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2307 | + * |
2308 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2309 | + */ |
2310 | + |
2311 | +#pragma once |
2312 | + |
2313 | +#include <QBuffer> |
2314 | +#include <QObject> |
2315 | +#include <QNetworkReply> |
2316 | +#include <unity/storage/provider/ProviderBase.h> |
2317 | + |
2318 | +#include <memory> |
2319 | + |
2320 | +class DavProvider; |
2321 | + |
2322 | +class DeleteHandler : public QObject { |
2323 | + Q_OBJECT |
2324 | +public: |
2325 | + DeleteHandler(std::shared_ptr<DavProvider> const& provider, std::string const& item_id, |
2326 | + unity::storage::provider::Context const& ctx); |
2327 | + ~DeleteHandler(); |
2328 | + |
2329 | + boost::future<void> get_future(); |
2330 | + |
2331 | +private Q_SLOTS: |
2332 | + void onFinished(); |
2333 | + |
2334 | +private: |
2335 | + boost::promise<void> promise_; |
2336 | + |
2337 | + std::shared_ptr<DavProvider> const provider_; |
2338 | + std::string const item_id_; |
2339 | + |
2340 | + std::unique_ptr<QNetworkReply> reply_; |
2341 | +}; |
2342 | |
2343 | === added file 'src/ListHandler.cpp' |
2344 | --- src/ListHandler.cpp 1970-01-01 00:00:00 +0000 |
2345 | +++ src/ListHandler.cpp 2016-11-25 05:01:23 +0000 |
2346 | @@ -0,0 +1,59 @@ |
2347 | +/* |
2348 | + * Copyright (C) 2016 Canonical Ltd. |
2349 | + * |
2350 | + * This program is free software: you can redistribute it and/or modify |
2351 | + * it under the terms of the GNU General Public License version 3 as |
2352 | + * published by the Free Software Foundation. |
2353 | + * |
2354 | + * This program is distributed in the hope that it will be useful, |
2355 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2356 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2357 | + * GNU General Public License for more details. |
2358 | + * |
2359 | + * You should have received a copy of the GNU General Public License |
2360 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2361 | + * |
2362 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2363 | + */ |
2364 | + |
2365 | +#include "ListHandler.h" |
2366 | + |
2367 | +#include <unity/storage/provider/Exceptions.h> |
2368 | + |
2369 | +#include <algorithm> |
2370 | +#include <cassert> |
2371 | + |
2372 | +using namespace std; |
2373 | +using namespace unity::storage::provider; |
2374 | + |
2375 | +ListHandler::ListHandler(std::shared_ptr<DavProvider> const& provider, |
2376 | + string const& parent_id, Context const& ctx) |
2377 | + : PropFindHandler(provider, parent_id, 1, ctx) |
2378 | +{ |
2379 | +} |
2380 | + |
2381 | +ListHandler::~ListHandler() = default; |
2382 | + |
2383 | +boost::future<tuple<ItemList,string>> ListHandler::get_future() |
2384 | +{ |
2385 | + return promise_.get_future(); |
2386 | +} |
2387 | + |
2388 | +void ListHandler::finish() |
2389 | +{ |
2390 | + deleteLater(); |
2391 | + |
2392 | + if (error_) |
2393 | + { |
2394 | + promise_.set_exception(error_); |
2395 | + return; |
2396 | + } |
2397 | + // A "Depth: 1" PROPFIND will also return data for the parent URL |
2398 | + // itself, so remove it from the list. |
2399 | + items_.erase(remove_if(items_.begin(), items_.end(), |
2400 | + [&](Item const& item) -> bool { |
2401 | + return item.item_id == item_id_; |
2402 | + }), items_.end()); |
2403 | + |
2404 | + promise_.set_value(make_tuple(move(items_), string())); |
2405 | +} |
2406 | |
2407 | === added file 'src/ListHandler.h' |
2408 | --- src/ListHandler.h 1970-01-01 00:00:00 +0000 |
2409 | +++ src/ListHandler.h 2016-11-25 05:01:23 +0000 |
2410 | @@ -0,0 +1,43 @@ |
2411 | +/* |
2412 | + * Copyright (C) 2016 Canonical Ltd. |
2413 | + * |
2414 | + * This program is free software: you can redistribute it and/or modify |
2415 | + * it under the terms of the GNU General Public License version 3 as |
2416 | + * published by the Free Software Foundation. |
2417 | + * |
2418 | + * This program is distributed in the hope that it will be useful, |
2419 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2420 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2421 | + * GNU General Public License for more details. |
2422 | + * |
2423 | + * You should have received a copy of the GNU General Public License |
2424 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2425 | + * |
2426 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2427 | + */ |
2428 | + |
2429 | +#pragma once |
2430 | + |
2431 | +#include <QObject> |
2432 | + |
2433 | +#include <memory> |
2434 | +#include <tuple> |
2435 | + |
2436 | +#include "PropFindHandler.h" |
2437 | + |
2438 | +class ListHandler : public PropFindHandler { |
2439 | + Q_OBJECT |
2440 | +public: |
2441 | + ListHandler(std::shared_ptr<DavProvider> const& provider, |
2442 | + std::string const& parent_id, |
2443 | + unity::storage::provider::Context const& ctx); |
2444 | + ~ListHandler(); |
2445 | + |
2446 | + boost::future<std::tuple<unity::storage::provider::ItemList,std::string>> get_future(); |
2447 | + |
2448 | +private: |
2449 | + boost::promise<std::tuple<unity::storage::provider::ItemList,std::string>> promise_; |
2450 | + |
2451 | +protected: |
2452 | + void finish() override; |
2453 | +}; |
2454 | |
2455 | === added file 'src/LookupHandler.cpp' |
2456 | --- src/LookupHandler.cpp 1970-01-01 00:00:00 +0000 |
2457 | +++ src/LookupHandler.cpp 2016-11-25 05:01:23 +0000 |
2458 | @@ -0,0 +1,54 @@ |
2459 | +/* |
2460 | + * Copyright (C) 2016 Canonical Ltd. |
2461 | + * |
2462 | + * This program is free software: you can redistribute it and/or modify |
2463 | + * it under the terms of the GNU General Public License version 3 as |
2464 | + * published by the Free Software Foundation. |
2465 | + * |
2466 | + * This program is distributed in the hope that it will be useful, |
2467 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2468 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2469 | + * GNU General Public License for more details. |
2470 | + * |
2471 | + * You should have received a copy of the GNU General Public License |
2472 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2473 | + * |
2474 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2475 | + */ |
2476 | + |
2477 | +#include "LookupHandler.h" |
2478 | + |
2479 | +#include <cassert> |
2480 | + |
2481 | +using namespace std; |
2482 | +using namespace unity::storage::provider; |
2483 | + |
2484 | +LookupHandler::LookupHandler(std::shared_ptr<DavProvider> const& provider, |
2485 | + string const& item_id, Context const& ctx) |
2486 | + : PropFindHandler(provider, item_id, 0, ctx) |
2487 | +{ |
2488 | +} |
2489 | + |
2490 | +LookupHandler::~LookupHandler() = default; |
2491 | + |
2492 | +boost::future<ItemList> LookupHandler::get_future() |
2493 | +{ |
2494 | + return promise_.get_future(); |
2495 | +} |
2496 | + |
2497 | +void LookupHandler::finish() |
2498 | +{ |
2499 | + if (error_) |
2500 | + { |
2501 | + promise_.set_exception(error_); |
2502 | + return; |
2503 | + } |
2504 | + // Perform some sanity checks on the result |
2505 | + if (items_.size() != 1) |
2506 | + { |
2507 | + promise_.set_exception(RemoteCommsException("Unexpectedly received " + to_string(items_.size()) + " items from PROPFIND request")); |
2508 | + return; |
2509 | + } |
2510 | + |
2511 | + promise_.set_value(move(items_)); |
2512 | +} |
2513 | |
2514 | === added file 'src/LookupHandler.h' |
2515 | --- src/LookupHandler.h 1970-01-01 00:00:00 +0000 |
2516 | +++ src/LookupHandler.h 2016-11-25 05:01:23 +0000 |
2517 | @@ -0,0 +1,42 @@ |
2518 | +/* |
2519 | + * Copyright (C) 2016 Canonical Ltd. |
2520 | + * |
2521 | + * This program is free software: you can redistribute it and/or modify |
2522 | + * it under the terms of the GNU General Public License version 3 as |
2523 | + * published by the Free Software Foundation. |
2524 | + * |
2525 | + * This program is distributed in the hope that it will be useful, |
2526 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2527 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2528 | + * GNU General Public License for more details. |
2529 | + * |
2530 | + * You should have received a copy of the GNU General Public License |
2531 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2532 | + * |
2533 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2534 | + */ |
2535 | + |
2536 | +#pragma once |
2537 | + |
2538 | +#include <QObject> |
2539 | + |
2540 | +#include <memory> |
2541 | + |
2542 | +#include "PropFindHandler.h" |
2543 | + |
2544 | +class LookupHandler : public PropFindHandler { |
2545 | + Q_OBJECT |
2546 | +public: |
2547 | + LookupHandler(std::shared_ptr<DavProvider> const& provider, |
2548 | + std::string const& item_id, |
2549 | + unity::storage::provider::Context const& ctx); |
2550 | + ~LookupHandler(); |
2551 | + |
2552 | + boost::future<unity::storage::provider::ItemList> get_future(); |
2553 | + |
2554 | +private: |
2555 | + boost::promise<unity::storage::provider::ItemList> promise_; |
2556 | + |
2557 | +protected: |
2558 | + void finish() override; |
2559 | +}; |
2560 | |
2561 | === added file 'src/MetadataHandler.cpp' |
2562 | --- src/MetadataHandler.cpp 1970-01-01 00:00:00 +0000 |
2563 | +++ src/MetadataHandler.cpp 2016-11-25 05:01:23 +0000 |
2564 | @@ -0,0 +1,61 @@ |
2565 | +/* |
2566 | + * Copyright (C) 2016 Canonical Ltd. |
2567 | + * |
2568 | + * This program is free software: you can redistribute it and/or modify |
2569 | + * it under the terms of the GNU General Public License version 3 as |
2570 | + * published by the Free Software Foundation. |
2571 | + * |
2572 | + * This program is distributed in the hope that it will be useful, |
2573 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2574 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2575 | + * GNU General Public License for more details. |
2576 | + * |
2577 | + * You should have received a copy of the GNU General Public License |
2578 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2579 | + * |
2580 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2581 | + */ |
2582 | + |
2583 | +#include "MetadataHandler.h" |
2584 | + |
2585 | +#include <cassert> |
2586 | + |
2587 | +using namespace std; |
2588 | +using namespace unity::storage::provider; |
2589 | + |
2590 | +MetadataHandler::MetadataHandler(shared_ptr<DavProvider> const& provider, |
2591 | + string const& item_id, Context const& ctx) |
2592 | + : PropFindHandler(provider, item_id, 0, ctx) |
2593 | +{ |
2594 | +} |
2595 | + |
2596 | +MetadataHandler::~MetadataHandler() = default; |
2597 | + |
2598 | +boost::future<Item> MetadataHandler::get_future() |
2599 | +{ |
2600 | + return promise_.get_future(); |
2601 | +} |
2602 | + |
2603 | +void MetadataHandler::finish() |
2604 | +{ |
2605 | + deleteLater(); |
2606 | + |
2607 | + if (error_) |
2608 | + { |
2609 | + promise_.set_exception(error_); |
2610 | + return; |
2611 | + } |
2612 | + // Perform some sanity checks on the result |
2613 | + if (items_.size() != 1) |
2614 | + { |
2615 | + promise_.set_exception(RemoteCommsException("Unexpectedly received " + to_string(items_.size()) + " items from PROPFIND request")); |
2616 | + return; |
2617 | + } |
2618 | + if (items_[0].item_id != item_id_) |
2619 | + { |
2620 | + promise_.set_exception(RemoteCommsException("PROPFIND request returned data about the wrong item")); |
2621 | + return; |
2622 | + } |
2623 | + |
2624 | + promise_.set_value(move(items_[0])); |
2625 | +} |
2626 | |
2627 | === added file 'src/MetadataHandler.h' |
2628 | --- src/MetadataHandler.h 1970-01-01 00:00:00 +0000 |
2629 | +++ src/MetadataHandler.h 2016-11-25 05:01:23 +0000 |
2630 | @@ -0,0 +1,42 @@ |
2631 | +/* |
2632 | + * Copyright (C) 2016 Canonical Ltd. |
2633 | + * |
2634 | + * This program is free software: you can redistribute it and/or modify |
2635 | + * it under the terms of the GNU General Public License version 3 as |
2636 | + * published by the Free Software Foundation. |
2637 | + * |
2638 | + * This program is distributed in the hope that it will be useful, |
2639 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2640 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2641 | + * GNU General Public License for more details. |
2642 | + * |
2643 | + * You should have received a copy of the GNU General Public License |
2644 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2645 | + * |
2646 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2647 | + */ |
2648 | + |
2649 | +#pragma once |
2650 | + |
2651 | +#include <QObject> |
2652 | + |
2653 | +#include <memory> |
2654 | + |
2655 | +#include "PropFindHandler.h" |
2656 | + |
2657 | +class MetadataHandler : public PropFindHandler { |
2658 | + Q_OBJECT |
2659 | +public: |
2660 | + MetadataHandler(std::shared_ptr<DavProvider> const& provider, |
2661 | + std::string const& item_id, |
2662 | + unity::storage::provider::Context const& ctx); |
2663 | + ~MetadataHandler(); |
2664 | + |
2665 | + boost::future<unity::storage::provider::Item> get_future(); |
2666 | + |
2667 | +private: |
2668 | + boost::promise<unity::storage::provider::Item> promise_; |
2669 | + |
2670 | +protected: |
2671 | + void finish() override; |
2672 | +}; |
2673 | |
2674 | === modified file 'src/MultiStatusParser.cpp' |
2675 | --- src/MultiStatusParser.cpp 2016-09-20 01:50:46 +0000 |
2676 | +++ src/MultiStatusParser.cpp 2016-11-25 05:01:23 +0000 |
2677 | @@ -1,3 +1,21 @@ |
2678 | +/* |
2679 | + * Copyright (C) 2016 Canonical Ltd. |
2680 | + * |
2681 | + * This program is free software: you can redistribute it and/or modify |
2682 | + * it under the terms of the GNU General Public License version 3 as |
2683 | + * published by the Free Software Foundation. |
2684 | + * |
2685 | + * This program is distributed in the hope that it will be useful, |
2686 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2687 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2688 | + * GNU General Public License for more details. |
2689 | + * |
2690 | + * You should have received a copy of the GNU General Public License |
2691 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2692 | + * |
2693 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2694 | + */ |
2695 | + |
2696 | #include "MultiStatusParser.h" |
2697 | |
2698 | #include <QDebug> |
2699 | @@ -127,10 +145,16 @@ |
2700 | { |
2701 | return; |
2702 | } |
2703 | - // Perform one final call to parseContinue() to signal end of file |
2704 | if (started_) |
2705 | { |
2706 | - reader_.parseContinue(); |
2707 | + // Drain the remaining data from the input channel |
2708 | + while (reader_.parseContinue()) |
2709 | + { |
2710 | + if (handler_->atEnd()) |
2711 | + { |
2712 | + break; |
2713 | + } |
2714 | + } |
2715 | } |
2716 | if (error_string_.isEmpty() && !handler_->atEnd()) |
2717 | { |
2718 | @@ -146,7 +170,6 @@ |
2719 | return true; |
2720 | } |
2721 | |
2722 | - |
2723 | bool MultiStatusParser::Handler::startElement(QString const& namespace_uri, |
2724 | QString const& local_name, |
2725 | QString const& qname, |
2726 | @@ -297,6 +320,7 @@ |
2727 | break; |
2728 | case ParseState::multistatus: |
2729 | state_ = ParseState::start; |
2730 | + at_end_ = true; |
2731 | break; |
2732 | case ParseState::response: |
2733 | Q_EMIT parser_->response(current_href_, current_properties_, |
2734 | @@ -318,7 +342,7 @@ |
2735 | for (auto& prop : current_propstat_) |
2736 | { |
2737 | prop.status = current_propstat_status_; |
2738 | - current_properties_.emplace_back(std::move(prop)); |
2739 | + current_properties_.emplace_back(move(prop)); |
2740 | } |
2741 | current_propstat_.clear(); |
2742 | state_ = ParseState::response; |
2743 | |
2744 | === modified file 'src/MultiStatusParser.h' |
2745 | --- src/MultiStatusParser.h 2016-09-20 01:50:46 +0000 |
2746 | +++ src/MultiStatusParser.h 2016-11-25 05:01:23 +0000 |
2747 | @@ -1,3 +1,21 @@ |
2748 | +/* |
2749 | + * Copyright (C) 2016 Canonical Ltd. |
2750 | + * |
2751 | + * This program is free software: you can redistribute it and/or modify |
2752 | + * it under the terms of the GNU General Public License version 3 as |
2753 | + * published by the Free Software Foundation. |
2754 | + * |
2755 | + * This program is distributed in the hope that it will be useful, |
2756 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2757 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2758 | + * GNU General Public License for more details. |
2759 | + * |
2760 | + * You should have received a copy of the GNU General Public License |
2761 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2762 | + * |
2763 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2764 | + */ |
2765 | + |
2766 | #pragma once |
2767 | |
2768 | #include <QIODevice> |
2769 | |
2770 | === modified file 'src/OwncloudProvider.cpp' |
2771 | --- src/OwncloudProvider.cpp 2016-09-22 06:40:07 +0000 |
2772 | +++ src/OwncloudProvider.cpp 2016-11-25 05:01:23 +0000 |
2773 | @@ -1,3 +1,21 @@ |
2774 | +/* |
2775 | + * Copyright (C) 2016 Canonical Ltd. |
2776 | + * |
2777 | + * This program is free software: you can redistribute it and/or modify |
2778 | + * it under the terms of the GNU General Public License version 3 as |
2779 | + * published by the Free Software Foundation. |
2780 | + * |
2781 | + * This program is distributed in the hope that it will be useful, |
2782 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2783 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2784 | + * GNU General Public License for more details. |
2785 | + * |
2786 | + * You should have received a copy of the GNU General Public License |
2787 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2788 | + * |
2789 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2790 | + */ |
2791 | + |
2792 | #include "OwncloudProvider.h" |
2793 | |
2794 | #include <QNetworkAccessManager> |
2795 | @@ -15,8 +33,8 @@ |
2796 | |
2797 | QUrl OwncloudProvider::base_url(Context const& ctx) const |
2798 | { |
2799 | - Q_UNUSED(ctx); |
2800 | - return QUrl("http://localhost:8080/"); |
2801 | + const auto& creds = boost::get<PasswordCredentials>(ctx.credentials); |
2802 | + return QUrl(QStringLiteral("http://localhost:8888/owncloud/remote.php/dav/files/%1/").arg(QString::fromStdString(creds.username))); |
2803 | } |
2804 | |
2805 | QNetworkReply *OwncloudProvider::send_request( |
2806 | @@ -25,9 +43,13 @@ |
2807 | { |
2808 | Q_UNUSED(ctx); |
2809 | |
2810 | - const auto credentials = QByteArrayLiteral("user:pass"); |
2811 | + const auto& creds = boost::get<PasswordCredentials>(ctx.credentials); |
2812 | + const auto credentials = QByteArray::fromStdString(creds.username + ":" + |
2813 | + creds.password); |
2814 | request.setRawHeader(QByteArrayLiteral("Authorization"), |
2815 | QByteArrayLiteral("Basic ") + credentials.toBase64()); |
2816 | + printf("Sending request to %s with credentials %s\n", |
2817 | + request.url().toEncoded().constData(), credentials.constData()); |
2818 | QNetworkReply *reply = network_->sendCustomRequest(request, verb, data); |
2819 | return reply; |
2820 | } |
2821 | |
2822 | === modified file 'src/OwncloudProvider.h' |
2823 | --- src/OwncloudProvider.h 2016-09-22 03:36:22 +0000 |
2824 | +++ src/OwncloudProvider.h 2016-11-25 05:01:23 +0000 |
2825 | @@ -1,3 +1,21 @@ |
2826 | +/* |
2827 | + * Copyright (C) 2016 Canonical Ltd. |
2828 | + * |
2829 | + * This program is free software: you can redistribute it and/or modify |
2830 | + * it under the terms of the GNU General Public License version 3 as |
2831 | + * published by the Free Software Foundation. |
2832 | + * |
2833 | + * This program is distributed in the hope that it will be useful, |
2834 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2835 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2836 | + * GNU General Public License for more details. |
2837 | + * |
2838 | + * You should have received a copy of the GNU General Public License |
2839 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2840 | + * |
2841 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2842 | + */ |
2843 | + |
2844 | #pragma once |
2845 | |
2846 | #include "DavProvider.h" |
2847 | |
2848 | === renamed file 'src/RootsHandler.cpp' => 'src/PropFindHandler.cpp' |
2849 | --- src/RootsHandler.cpp 2016-09-27 06:28:45 +0000 |
2850 | +++ src/PropFindHandler.cpp 2016-11-25 05:01:23 +0000 |
2851 | @@ -1,15 +1,29 @@ |
2852 | -#include "RootsHandler.h" |
2853 | +/* |
2854 | + * Copyright (C) 2016 Canonical Ltd. |
2855 | + * |
2856 | + * This program is free software: you can redistribute it and/or modify |
2857 | + * it under the terms of the GNU General Public License version 3 as |
2858 | + * published by the Free Software Foundation. |
2859 | + * |
2860 | + * This program is distributed in the hope that it will be useful, |
2861 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2862 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2863 | + * GNU General Public License for more details. |
2864 | + * |
2865 | + * You should have received a copy of the GNU General Public License |
2866 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2867 | + * |
2868 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
2869 | + */ |
2870 | + |
2871 | +#include "PropFindHandler.h" |
2872 | #include "item_id.h" |
2873 | - |
2874 | -#include <QDateTime> |
2875 | -#include <QDebug> |
2876 | -#include <unity/storage/provider/metadata_keys.h> |
2877 | +#include "http_error.h" |
2878 | |
2879 | #include <cassert> |
2880 | |
2881 | using namespace std; |
2882 | using namespace unity::storage::provider; |
2883 | -using unity::storage::ItemType; |
2884 | |
2885 | namespace |
2886 | { |
2887 | @@ -26,161 +40,143 @@ |
2888 | </D:propfind>)"); |
2889 | } |
2890 | |
2891 | -RootsHandler::RootsHandler(DavProvider const& provider, Context const& ctx) |
2892 | - : provider_(provider), base_url_(provider.base_url(ctx)) |
2893 | +PropFindHandler::PropFindHandler(shared_ptr<DavProvider> const& provider, |
2894 | + string const& item_id, int depth, |
2895 | + Context const& ctx) |
2896 | + : provider_(provider), base_url_(provider->base_url(ctx)), item_id_(item_id) |
2897 | { |
2898 | - QNetworkRequest request(base_url_); |
2899 | - request.setRawHeader(QByteArrayLiteral("Depth"), QByteArrayLiteral("0")); |
2900 | + QNetworkRequest request(id_to_url(item_id_, base_url_)); |
2901 | + request.setRawHeader(QByteArrayLiteral("Depth"), QByteArray::number(depth)); |
2902 | request.setHeader(QNetworkRequest::ContentTypeHeader, |
2903 | QStringLiteral("application/xml; charset=\"utf-8\"")); |
2904 | request.setHeader(QNetworkRequest::ContentLengthHeader, PROPFIND_BODY.size()); |
2905 | request_body_.setData(PROPFIND_BODY); |
2906 | request_body_.open(QIODevice::ReadOnly); |
2907 | |
2908 | - reply_.reset(provider.send_request(request, QByteArrayLiteral("PROPFIND"), |
2909 | - &request_body_, ctx)); |
2910 | + reply_.reset(provider->send_request(request, QByteArrayLiteral("PROPFIND"), |
2911 | + &request_body_, ctx)); |
2912 | assert(reply_.get() != nullptr); |
2913 | |
2914 | - connect(reply_.get(), static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), |
2915 | - this, &RootsHandler::onError); |
2916 | connect(reply_.get(), &QIODevice::readyRead, |
2917 | - this, &RootsHandler::onReadyRead); |
2918 | -} |
2919 | - |
2920 | -RootsHandler::~RootsHandler() |
2921 | -{ |
2922 | -} |
2923 | - |
2924 | -boost::future<unity::storage::provider::ItemList> RootsHandler::get_future() |
2925 | -{ |
2926 | - return promise_.get_future(); |
2927 | -} |
2928 | - |
2929 | -void RootsHandler::sendError(StorageException const& error) |
2930 | -{ |
2931 | - if (promise_set_) { |
2932 | - return; |
2933 | - } |
2934 | - |
2935 | - promise_.set_exception(error); |
2936 | - promise_set_ = true; |
2937 | - |
2938 | - deleteLater(); |
2939 | -} |
2940 | - |
2941 | -void RootsHandler::sendResponse() |
2942 | -{ |
2943 | - if (promise_set_) { |
2944 | - return; |
2945 | - } |
2946 | - |
2947 | - promise_.set_value(roots_); |
2948 | - promise_set_ = true; |
2949 | - |
2950 | - deleteLater(); |
2951 | -} |
2952 | - |
2953 | -void RootsHandler::onError(QNetworkReply::NetworkError code) |
2954 | -{ |
2955 | - sendError(RemoteCommsException("Error from QNetworkReply: " + |
2956 | - to_string(code))); |
2957 | -} |
2958 | - |
2959 | -void RootsHandler::onReadyRead() |
2960 | -{ |
2961 | - disconnect(reply_.get(), &QIODevice::readyRead, |
2962 | - this, &RootsHandler::onReadyRead); |
2963 | - auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
2964 | - |
2965 | - if (status != 207) |
2966 | - { |
2967 | - sendError(RemoteCommsException("Expected 207 response, but got " + to_string(status))); |
2968 | - return; |
2969 | - } |
2970 | - parser_.reset(new MultiStatusParser(reply_->request().url(), reply_.get())); |
2971 | - connect(parser_.get(), &MultiStatusParser::response, |
2972 | - this, &RootsHandler::onParserResponse); |
2973 | - connect(parser_.get(), &MultiStatusParser::finished, |
2974 | - this, &RootsHandler::onParserFinished); |
2975 | -} |
2976 | - |
2977 | -void RootsHandler::onParserResponse(QUrl const& href, std::vector<MultiStatusProperty> const& properties, int status) |
2978 | -{ |
2979 | - if (href != base_url_) |
2980 | - { |
2981 | - sendError(RemoteCommsException("PROPFIND returned information about a different URL")); |
2982 | - return; |
2983 | - } |
2984 | + this, &PropFindHandler::onReplyReadyRead); |
2985 | + connect(reply_.get(), &QNetworkReply::finished, |
2986 | + this, &PropFindHandler::onReplyFinished); |
2987 | +} |
2988 | + |
2989 | +PropFindHandler::~PropFindHandler() |
2990 | +{ |
2991 | +} |
2992 | + |
2993 | +void PropFindHandler::abort() |
2994 | +{ |
2995 | + if (finished_) |
2996 | + { |
2997 | + return; |
2998 | + } |
2999 | + finished_ = true; |
3000 | + reply_->abort(); |
3001 | +} |
3002 | + |
3003 | +void PropFindHandler::reportError(StorageException const& error) |
3004 | +{ |
3005 | + reportError(boost::copy_exception(error)); |
3006 | +} |
3007 | + |
3008 | +void PropFindHandler::reportError(boost::exception_ptr const& ep) |
3009 | +{ |
3010 | + if (finished_) |
3011 | + { |
3012 | + return; |
3013 | + } |
3014 | + |
3015 | + error_ = ep; |
3016 | + finished_ = true; |
3017 | + finish(); |
3018 | +} |
3019 | + |
3020 | +void PropFindHandler::reportSuccess() |
3021 | +{ |
3022 | + if (finished_) |
3023 | + { |
3024 | + return; |
3025 | + } |
3026 | + |
3027 | + finished_ = true; |
3028 | + finish(); |
3029 | +} |
3030 | + |
3031 | +void PropFindHandler::onReplyReadyRead() |
3032 | +{ |
3033 | + if (!seen_headers_) |
3034 | + { |
3035 | + seen_headers_ = true; |
3036 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
3037 | + if (status == 207) |
3038 | + { |
3039 | + disconnect(reply_.get(), &QIODevice::readyRead, |
3040 | + this, &PropFindHandler::onReplyReadyRead); |
3041 | + parser_.reset(new MultiStatusParser(reply_->request().url(), reply_.get())); |
3042 | + connect(parser_.get(), &MultiStatusParser::response, |
3043 | + this, &PropFindHandler::onParserResponse); |
3044 | + connect(parser_.get(), &MultiStatusParser::finished, |
3045 | + this, &PropFindHandler::onParserFinished); |
3046 | + } |
3047 | + else |
3048 | + { |
3049 | + is_error_ = true; |
3050 | + } |
3051 | + } |
3052 | + if (is_error_) |
3053 | + { |
3054 | + if (error_body_.size() < MAX_ERROR_BODY_LENGTH) |
3055 | + { |
3056 | + error_body_.append(reply_->readAll()); |
3057 | + } |
3058 | + else |
3059 | + { |
3060 | + reply_->close(); |
3061 | + } |
3062 | + } |
3063 | +} |
3064 | + |
3065 | +void PropFindHandler::onReplyFinished() |
3066 | +{ |
3067 | + if (!seen_headers_ || is_error_) |
3068 | + { |
3069 | + reportError(translate_http_error(reply_.get(), error_body_, item_id_)); |
3070 | + } |
3071 | +} |
3072 | + |
3073 | +void PropFindHandler::onParserResponse(QUrl const& href, vector<MultiStatusProperty> const& properties, int status) |
3074 | +{ |
3075 | if (status != 0 && status != 200) |
3076 | { |
3077 | - sendError(RemoteCommsException("Failed to look up properties for root: " + to_string(status))); |
3078 | + reportError(RemoteCommsException("PROPFIND for " + href.toEncoded().toStdString() + " gave status " + to_string(status))); |
3079 | return; |
3080 | } |
3081 | try |
3082 | { |
3083 | - Item item; |
3084 | - item.item_id = url_to_id(href, base_url_); |
3085 | - item.name = "Root"; |
3086 | - item.type = ItemType::root; |
3087 | - |
3088 | - for (const auto& prop : properties) |
3089 | - { |
3090 | - if (prop.status != 200) { |
3091 | - // Don't warn about "404 Not Found" properties |
3092 | - if (prop.status != 404) { |
3093 | - qWarning() << "Got status" << prop.status << "for property" |
3094 | - << prop.ns << prop.name; |
3095 | - } |
3096 | - continue; |
3097 | - } |
3098 | - if (prop.ns == "DAV:") |
3099 | - { |
3100 | - if (prop.name == "getetag") |
3101 | - { |
3102 | - item.etag = prop.value.toStdString(); |
3103 | - } |
3104 | - else if (prop.name == "getcontentlength") |
3105 | - { |
3106 | - item.metadata[SIZE_IN_BYTES] = static_cast<int64_t>(prop.value.toLongLong()); |
3107 | - } |
3108 | - else if (prop.name == "creationdate") |
3109 | - { |
3110 | - auto date = QDateTime::fromString(prop.value, Qt::RFC2822Date); |
3111 | - if (date.isValid()) |
3112 | - { |
3113 | - item.metadata[CREATION_TIME] = date.toString(Qt::ISODate).toStdString(); |
3114 | - } |
3115 | - } |
3116 | - else if (prop.name == "getlastmodified") |
3117 | - { |
3118 | - auto date = QDateTime::fromString(prop.value, Qt::RFC2822Date); |
3119 | - if (date.isValid()) |
3120 | - { |
3121 | - item.metadata[LAST_MODIFIED_TIME] = date.toString(Qt::ISODate).toStdString(); |
3122 | - } |
3123 | - } |
3124 | - } |
3125 | - } |
3126 | - roots_.emplace_back(std::move(item)); |
3127 | + Item item = provider_->make_item(href, base_url_, properties); |
3128 | + items_.emplace_back(move(item)); |
3129 | } |
3130 | catch (StorageException const& error) |
3131 | { |
3132 | - sendError(error); |
3133 | + reportError(error); |
3134 | } |
3135 | catch (exception const& error) |
3136 | { |
3137 | - sendError(RemoteCommsException(string("Error creating item: ") + error.what())); |
3138 | + reportError(RemoteCommsException(string("Error creating item: ") + error.what())); |
3139 | } |
3140 | } |
3141 | |
3142 | -void RootsHandler::onParserFinished() |
3143 | +void PropFindHandler::onParserFinished() |
3144 | { |
3145 | if (parser_->errorString().isEmpty()) |
3146 | { |
3147 | - sendResponse(); |
3148 | + reportSuccess(); |
3149 | } |
3150 | else |
3151 | { |
3152 | - sendError(RemoteCommsException("Error parsing Multi-Status response: " + parser_->errorString().toStdString())); |
3153 | + reportError(RemoteCommsException("Error parsing Multi-Status response: " + parser_->errorString().toStdString())); |
3154 | } |
3155 | } |
3156 | |
3157 | === renamed file 'src/RootsHandler.h' => 'src/PropFindHandler.h' |
3158 | --- src/RootsHandler.h 2016-09-22 03:36:22 +0000 |
3159 | +++ src/PropFindHandler.h 2016-11-25 05:01:23 +0000 |
3160 | @@ -1,3 +1,21 @@ |
3161 | +/* |
3162 | + * Copyright (C) 2016 Canonical Ltd. |
3163 | + * |
3164 | + * This program is free software: you can redistribute it and/or modify |
3165 | + * it under the terms of the GNU General Public License version 3 as |
3166 | + * published by the Free Software Foundation. |
3167 | + * |
3168 | + * This program is distributed in the hope that it will be useful, |
3169 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3170 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3171 | + * GNU General Public License for more details. |
3172 | + * |
3173 | + * You should have received a copy of the GNU General Public License |
3174 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3175 | + * |
3176 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3177 | + */ |
3178 | + |
3179 | #pragma once |
3180 | |
3181 | #include <QBuffer> |
3182 | @@ -10,37 +28,46 @@ |
3183 | #include "DavProvider.h" |
3184 | #include "MultiStatusParser.h" |
3185 | |
3186 | -class RootsHandler : public QObject { |
3187 | +class PropFindHandler : public QObject { |
3188 | Q_OBJECT |
3189 | public: |
3190 | - RootsHandler(DavProvider const& provider, |
3191 | - unity::storage::provider::Context const& ctx); |
3192 | - ~RootsHandler(); |
3193 | - |
3194 | - boost::future<unity::storage::provider::ItemList> get_future(); |
3195 | - |
3196 | -private: |
3197 | - void sendError(unity::storage::provider::StorageException const& error); |
3198 | - void sendResponse(); |
3199 | + PropFindHandler(std::shared_ptr<DavProvider> const& provider, |
3200 | + std::string const& item_id, int depth, |
3201 | + unity::storage::provider::Context const& ctx); |
3202 | + ~PropFindHandler(); |
3203 | + |
3204 | + void abort(); |
3205 | |
3206 | private Q_SLOTS: |
3207 | // From QNetworkReply |
3208 | - void onError(QNetworkReply::NetworkError code); |
3209 | - void onReadyRead(); |
3210 | + void onReplyReadyRead(); |
3211 | + void onReplyFinished(); |
3212 | |
3213 | // From MultiStatusParser |
3214 | void onParserResponse(QUrl const& href, std::vector<MultiStatusProperty> const& properties, int status); |
3215 | void onParserFinished(); |
3216 | |
3217 | private: |
3218 | - bool promise_set_ = false; |
3219 | + void reportError(unity::storage::provider::StorageException const& error); |
3220 | + void reportError(boost::exception_ptr const& ep); |
3221 | + void reportSuccess(); |
3222 | + |
3223 | + bool seen_headers_ = false; |
3224 | + bool is_error_ = false; |
3225 | + bool finished_ = false; |
3226 | boost::promise<unity::storage::provider::ItemList> promise_; |
3227 | |
3228 | - DavProvider const& provider_; |
3229 | + std::shared_ptr<DavProvider> const provider_; |
3230 | QUrl base_url_; |
3231 | QBuffer request_body_; |
3232 | std::unique_ptr<QNetworkReply> reply_; |
3233 | - |
3234 | std::unique_ptr<MultiStatusParser> parser_; |
3235 | - unity::storage::provider::ItemList roots_; |
3236 | + QByteArray error_body_; |
3237 | + |
3238 | +protected: |
3239 | + virtual void finish() = 0; |
3240 | + |
3241 | + std::string const item_id_; |
3242 | + unity::storage::provider::ItemList items_; |
3243 | + boost::exception_ptr error_; |
3244 | }; |
3245 | |
3246 | === added file 'src/RetrieveMetadataHandler.cpp' |
3247 | --- src/RetrieveMetadataHandler.cpp 1970-01-01 00:00:00 +0000 |
3248 | +++ src/RetrieveMetadataHandler.cpp 2016-11-25 05:01:23 +0000 |
3249 | @@ -0,0 +1,52 @@ |
3250 | +/* |
3251 | + * Copyright (C) 2016 Canonical Ltd. |
3252 | + * |
3253 | + * This program is free software: you can redistribute it and/or modify |
3254 | + * it under the terms of the GNU General Public License version 3 as |
3255 | + * published by the Free Software Foundation. |
3256 | + * |
3257 | + * This program is distributed in the hope that it will be useful, |
3258 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3259 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3260 | + * GNU General Public License for more details. |
3261 | + * |
3262 | + * You should have received a copy of the GNU General Public License |
3263 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3264 | + * |
3265 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3266 | + */ |
3267 | + |
3268 | +#include "RetrieveMetadataHandler.h" |
3269 | + |
3270 | +#include <unity/storage/provider/Exceptions.h> |
3271 | + |
3272 | +#include <cassert> |
3273 | + |
3274 | +using namespace std; |
3275 | +using namespace unity::storage::provider; |
3276 | + |
3277 | +RetrieveMetadataHandler::RetrieveMetadataHandler(shared_ptr<DavProvider> const& provider, |
3278 | + string const& item_id, |
3279 | + Context const& ctx, |
3280 | + Callback callback) |
3281 | + : PropFindHandler(provider, item_id, 0, ctx), callback_(callback) |
3282 | +{ |
3283 | +} |
3284 | + |
3285 | +RetrieveMetadataHandler::~RetrieveMetadataHandler() = default; |
3286 | + |
3287 | +void RetrieveMetadataHandler::finish() |
3288 | +{ |
3289 | + Item item; |
3290 | + boost::exception_ptr ex = error_; |
3291 | + |
3292 | + if (items_.size() != 1) |
3293 | + { |
3294 | + ex = boost::copy_exception(RemoteCommsException("Unexpectedly received " + to_string(items_.size()) + " items from PROPFIND request")); |
3295 | + } |
3296 | + else |
3297 | + { |
3298 | + item = items_[0]; |
3299 | + } |
3300 | + callback_(item, ex); |
3301 | +} |
3302 | |
3303 | === added file 'src/RetrieveMetadataHandler.h' |
3304 | --- src/RetrieveMetadataHandler.h 1970-01-01 00:00:00 +0000 |
3305 | +++ src/RetrieveMetadataHandler.h 2016-11-25 05:01:23 +0000 |
3306 | @@ -0,0 +1,45 @@ |
3307 | +/* |
3308 | + * Copyright (C) 2016 Canonical Ltd. |
3309 | + * |
3310 | + * This program is free software: you can redistribute it and/or modify |
3311 | + * it under the terms of the GNU General Public License version 3 as |
3312 | + * published by the Free Software Foundation. |
3313 | + * |
3314 | + * This program is distributed in the hope that it will be useful, |
3315 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3316 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3317 | + * GNU General Public License for more details. |
3318 | + * |
3319 | + * You should have received a copy of the GNU General Public License |
3320 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3321 | + * |
3322 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3323 | + */ |
3324 | + |
3325 | +#pragma once |
3326 | + |
3327 | +#include <QObject> |
3328 | + |
3329 | +#include <functional> |
3330 | +#include <memory> |
3331 | + |
3332 | +#include "PropFindHandler.h" |
3333 | + |
3334 | +class RetrieveMetadataHandler : public PropFindHandler { |
3335 | + Q_OBJECT |
3336 | +public: |
3337 | + typedef std::function<void(unity::storage::provider::Item const& item, |
3338 | + boost::exception_ptr const& error)> Callback; |
3339 | + |
3340 | + RetrieveMetadataHandler(std::shared_ptr<DavProvider> const& provider, |
3341 | + std::string const& item_id, |
3342 | + unity::storage::provider::Context const& ctx, |
3343 | + Callback callback); |
3344 | + ~RetrieveMetadataHandler(); |
3345 | + |
3346 | +private: |
3347 | + Callback const callback_; |
3348 | + |
3349 | +protected: |
3350 | + void finish() override; |
3351 | +}; |
3352 | |
3353 | === added file 'src/RootsHandler.cpp' |
3354 | --- src/RootsHandler.cpp 1970-01-01 00:00:00 +0000 |
3355 | +++ src/RootsHandler.cpp 2016-11-25 05:01:23 +0000 |
3356 | @@ -0,0 +1,61 @@ |
3357 | +/* |
3358 | + * Copyright (C) 2016 Canonical Ltd. |
3359 | + * |
3360 | + * This program is free software: you can redistribute it and/or modify |
3361 | + * it under the terms of the GNU General Public License version 3 as |
3362 | + * published by the Free Software Foundation. |
3363 | + * |
3364 | + * This program is distributed in the hope that it will be useful, |
3365 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3366 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3367 | + * GNU General Public License for more details. |
3368 | + * |
3369 | + * You should have received a copy of the GNU General Public License |
3370 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3371 | + * |
3372 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3373 | + */ |
3374 | + |
3375 | +#include "RootsHandler.h" |
3376 | + |
3377 | +#include <cassert> |
3378 | + |
3379 | +using namespace std; |
3380 | +using namespace unity::storage::provider; |
3381 | + |
3382 | +RootsHandler::RootsHandler(shared_ptr<DavProvider> const& provider, |
3383 | + Context const& ctx) |
3384 | + : PropFindHandler(provider, ".", 0, ctx) |
3385 | +{ |
3386 | +} |
3387 | + |
3388 | +RootsHandler::~RootsHandler() = default; |
3389 | + |
3390 | +boost::future<ItemList> RootsHandler::get_future() |
3391 | +{ |
3392 | + return promise_.get_future(); |
3393 | +} |
3394 | + |
3395 | +void RootsHandler::finish() |
3396 | +{ |
3397 | + deleteLater(); |
3398 | + |
3399 | + if (error_) |
3400 | + { |
3401 | + promise_.set_exception(error_); |
3402 | + return; |
3403 | + } |
3404 | + // Perform some sanity checks on the result |
3405 | + if (items_.size() != 1) |
3406 | + { |
3407 | + promise_.set_exception(RemoteCommsException("Unexpectedly received " + to_string(items_.size()) + " items from PROPFIND request")); |
3408 | + return; |
3409 | + } |
3410 | + if (items_[0].item_id != ".") |
3411 | + { |
3412 | + promise_.set_exception(RemoteCommsException("PROPFIND request returned data about the wrong item")); |
3413 | + return; |
3414 | + } |
3415 | + |
3416 | + promise_.set_value(move(items_)); |
3417 | +} |
3418 | |
3419 | === added file 'src/RootsHandler.h' |
3420 | --- src/RootsHandler.h 1970-01-01 00:00:00 +0000 |
3421 | +++ src/RootsHandler.h 2016-11-25 05:01:23 +0000 |
3422 | @@ -0,0 +1,41 @@ |
3423 | +/* |
3424 | + * Copyright (C) 2016 Canonical Ltd. |
3425 | + * |
3426 | + * This program is free software: you can redistribute it and/or modify |
3427 | + * it under the terms of the GNU General Public License version 3 as |
3428 | + * published by the Free Software Foundation. |
3429 | + * |
3430 | + * This program is distributed in the hope that it will be useful, |
3431 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3432 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3433 | + * GNU General Public License for more details. |
3434 | + * |
3435 | + * You should have received a copy of the GNU General Public License |
3436 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3437 | + * |
3438 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3439 | + */ |
3440 | + |
3441 | +#pragma once |
3442 | + |
3443 | +#include <QObject> |
3444 | + |
3445 | +#include <memory> |
3446 | + |
3447 | +#include "PropFindHandler.h" |
3448 | + |
3449 | +class RootsHandler : public PropFindHandler { |
3450 | + Q_OBJECT |
3451 | +public: |
3452 | + RootsHandler(std::shared_ptr<DavProvider> const& provider, |
3453 | + unity::storage::provider::Context const& ctx); |
3454 | + ~RootsHandler(); |
3455 | + |
3456 | + boost::future<unity::storage::provider::ItemList> get_future(); |
3457 | + |
3458 | +private: |
3459 | + boost::promise<unity::storage::provider::ItemList> promise_; |
3460 | + |
3461 | +protected: |
3462 | + void finish() override; |
3463 | +}; |
3464 | |
3465 | === added file 'src/http_error.cpp' |
3466 | --- src/http_error.cpp 1970-01-01 00:00:00 +0000 |
3467 | +++ src/http_error.cpp 2016-11-25 05:01:23 +0000 |
3468 | @@ -0,0 +1,181 @@ |
3469 | +/* |
3470 | + * Copyright (C) 2016 Canonical Ltd. |
3471 | + * |
3472 | + * This program is free software: you can redistribute it and/or modify |
3473 | + * it under the terms of the GNU General Public License version 3 as |
3474 | + * published by the Free Software Foundation. |
3475 | + * |
3476 | + * This program is distributed in the hope that it will be useful, |
3477 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3478 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3479 | + * GNU General Public License for more details. |
3480 | + * |
3481 | + * You should have received a copy of the GNU General Public License |
3482 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3483 | + * |
3484 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3485 | + */ |
3486 | + |
3487 | +#include "http_error.h" |
3488 | + |
3489 | +#include <QNetworkReply> |
3490 | +#include <QXmlDefaultHandler> |
3491 | +#include <QXmlInputSource> |
3492 | +#include <QXmlSimpleReader> |
3493 | +#include <unity/storage/provider/Exceptions.h> |
3494 | + |
3495 | +#include <cassert> |
3496 | + |
3497 | +using namespace std; |
3498 | +using namespace unity::storage::provider; |
3499 | + |
3500 | +namespace { |
3501 | + |
3502 | +class ErrorParser : public QXmlDefaultHandler |
3503 | +{ |
3504 | +public: |
3505 | + bool startElement(QString const& namespace_uri, QString const& local_name, |
3506 | + QString const& qname, QXmlAttributes const& attrs) override; |
3507 | + bool endElement(QString const& namespace_uri, QString const& local_name, |
3508 | + QString const& qname) override; |
3509 | + bool characters(QString const& data) override; |
3510 | + |
3511 | + string exception_; |
3512 | + string message_; |
3513 | + |
3514 | +private: |
3515 | + static QString const SABREDAV_NS; |
3516 | + |
3517 | + QString char_data_; |
3518 | +}; |
3519 | + |
3520 | +QString const ErrorParser::SABREDAV_NS("http://sabredav.org/ns"); |
3521 | + |
3522 | +bool ErrorParser::startElement(QString const& namespace_uri, |
3523 | + QString const& local_name, |
3524 | + QString const& qname, |
3525 | + QXmlAttributes const& attrs) |
3526 | +{ |
3527 | + Q_UNUSED(namespace_uri); |
3528 | + Q_UNUSED(local_name); |
3529 | + Q_UNUSED(qname); |
3530 | + Q_UNUSED(attrs); |
3531 | + |
3532 | + char_data_.clear(); |
3533 | + return true; |
3534 | +} |
3535 | + |
3536 | +bool ErrorParser::endElement(QString const& namespace_uri, |
3537 | + QString const& local_name, |
3538 | + QString const& qname) |
3539 | +{ |
3540 | + Q_UNUSED(qname); |
3541 | + if (namespace_uri == SABREDAV_NS) |
3542 | + { |
3543 | + if (local_name == "exception") |
3544 | + { |
3545 | + exception_ = char_data_.toStdString(); |
3546 | + } |
3547 | + else if (local_name == "message") |
3548 | + { |
3549 | + message_ = char_data_.toStdString(); |
3550 | + } |
3551 | + } |
3552 | + char_data_.clear(); |
3553 | + return true; |
3554 | +} |
3555 | + |
3556 | +bool ErrorParser::characters(QString const& data) |
3557 | +{ |
3558 | + char_data_ += data; |
3559 | + return true; |
3560 | +} |
3561 | + |
3562 | +std::string make_message(QNetworkReply *reply, |
3563 | + QByteArray body) |
3564 | +{ |
3565 | + if (body.isEmpty()) |
3566 | + { |
3567 | + body = reply->readAll(); |
3568 | + } |
3569 | + auto content_type = reply->header(QNetworkRequest::ContentTypeHeader).toString(); |
3570 | + int semicolon = content_type.indexOf(';'); |
3571 | + if (semicolon >= 0) |
3572 | + { |
3573 | + content_type = content_type.left(semicolon); |
3574 | + } |
3575 | + if (content_type == "text/plain") |
3576 | + { |
3577 | + return body.toStdString(); |
3578 | + } |
3579 | + else if (content_type == "application/xml") |
3580 | + { |
3581 | + ErrorParser parser; |
3582 | + QXmlSimpleReader reader; |
3583 | + QXmlInputSource source; |
3584 | + reader.setContentHandler(&parser); |
3585 | + source.setData(body); |
3586 | + if (reader.parse(source)) { |
3587 | + return parser.exception_ + ": " + parser.message_; |
3588 | + } |
3589 | + } |
3590 | + // Fall back to returning the HTTP status string |
3591 | + return reply->attribute( |
3592 | + QNetworkRequest::HttpReasonPhraseAttribute).toString().toStdString(); |
3593 | +} |
3594 | + |
3595 | +} |
3596 | + |
3597 | +boost::exception_ptr translate_http_error(QNetworkReply *reply, |
3598 | + QByteArray const& body, |
3599 | + string const& item_id) |
3600 | +{ |
3601 | + assert(reply != nullptr); |
3602 | + assert(reply->isFinished()); |
3603 | + |
3604 | + auto status = reply->attribute( |
3605 | + QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
3606 | + if (status == 0) |
3607 | + { |
3608 | + return boost::copy_exception( |
3609 | + RemoteCommsException(reply->errorString().toStdString())); |
3610 | + } |
3611 | + |
3612 | + auto message = make_message(reply, body); |
3613 | + |
3614 | + switch (status) |
3615 | + { |
3616 | + case 400: // Bad Request |
3617 | + return boost::copy_exception(RemoteCommsException(message)); |
3618 | + |
3619 | + case 401: // Unauthorised |
3620 | + // This should be separate from Forbidden, triggering reauth. |
3621 | + case 403: // Forbidden |
3622 | + case 451: // Unavailable for Legal Reasons |
3623 | + return boost::copy_exception(PermissionException(message)); |
3624 | + |
3625 | + case 404: // Not found |
3626 | + case 410: // Gone |
3627 | + return boost::copy_exception(NotExistsException(message, item_id)); |
3628 | + |
3629 | + case 405: // Method Not Allowed |
3630 | + // MKCOL on an existing resource results in a 405 error |
3631 | + if (reply->request().attribute(QNetworkRequest::CustomVerbAttribute) == "MKCOL") |
3632 | + { |
3633 | + return boost::copy_exception(ExistsException(message, item_id, "fixme")); |
3634 | + } |
3635 | + break; |
3636 | + |
3637 | + case 409: // Conflict |
3638 | + case 412: // Precondition failed |
3639 | + return boost::copy_exception(ConflictException(message)); |
3640 | + |
3641 | + case 507: // Insufficient Storage |
3642 | + return boost::copy_exception(QuotaException(message)); |
3643 | + |
3644 | + default: |
3645 | + break; |
3646 | + } |
3647 | + return boost::copy_exception( |
3648 | + UnknownException("HTTP " + to_string(status) + ": " + message)); |
3649 | +} |
3650 | |
3651 | === added file 'src/http_error.h' |
3652 | --- src/http_error.h 1970-01-01 00:00:00 +0000 |
3653 | +++ src/http_error.h 2016-11-25 05:01:23 +0000 |
3654 | @@ -0,0 +1,30 @@ |
3655 | +/* |
3656 | + * Copyright (C) 2016 Canonical Ltd. |
3657 | + * |
3658 | + * This program is free software: you can redistribute it and/or modify |
3659 | + * it under the terms of the GNU General Public License version 3 as |
3660 | + * published by the Free Software Foundation. |
3661 | + * |
3662 | + * This program is distributed in the hope that it will be useful, |
3663 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3664 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3665 | + * GNU General Public License for more details. |
3666 | + * |
3667 | + * You should have received a copy of the GNU General Public License |
3668 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3669 | + * |
3670 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3671 | + */ |
3672 | + |
3673 | +#pragma once |
3674 | + |
3675 | +#include <boost/thread.hpp> |
3676 | + |
3677 | +class QByteArray; |
3678 | +class QNetworkReply; |
3679 | + |
3680 | +static constexpr int MAX_ERROR_BODY_LENGTH = 64 * 1024; |
3681 | + |
3682 | +boost::exception_ptr translate_http_error(QNetworkReply *reply, |
3683 | + QByteArray const& body, |
3684 | + std::string const& item_id={}); |
3685 | |
3686 | === modified file 'src/item_id.cpp' |
3687 | --- src/item_id.cpp 2016-09-18 23:12:40 +0000 |
3688 | +++ src/item_id.cpp 2016-11-25 05:01:23 +0000 |
3689 | @@ -1,3 +1,21 @@ |
3690 | +/* |
3691 | + * Copyright (C) 2016 Canonical Ltd. |
3692 | + * |
3693 | + * This program is free software: you can redistribute it and/or modify |
3694 | + * it under the terms of the GNU General Public License version 3 as |
3695 | + * published by the Free Software Foundation. |
3696 | + * |
3697 | + * This program is distributed in the hope that it will be useful, |
3698 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3699 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3700 | + * GNU General Public License for more details. |
3701 | + * |
3702 | + * You should have received a copy of the GNU General Public License |
3703 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3704 | + * |
3705 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3706 | + */ |
3707 | + |
3708 | #include "item_id.h" |
3709 | |
3710 | #include <unity/storage/provider/Exceptions.h> |
3711 | @@ -44,3 +62,44 @@ |
3712 | } |
3713 | return item_id; |
3714 | } |
3715 | + |
3716 | +// Construct a possible ID for a named child of a parent ID |
3717 | +string make_child_id(string const& parent_id, string const& name, bool is_folder) |
3718 | +{ |
3719 | + if (name == "." || name == "..") |
3720 | + { |
3721 | + throw InvalidArgumentException("Invalid name: " + name); |
3722 | + } |
3723 | + |
3724 | + string item_id = parent_id; |
3725 | + if (item_id == ".") |
3726 | + { |
3727 | + item_id.clear(); |
3728 | + } |
3729 | + else if (item_id.size() == 0 || item_id[item_id.size()-1] != '/') |
3730 | + { |
3731 | + item_id += '/'; |
3732 | + } |
3733 | + item_id += QUrl::toPercentEncoding(QString::fromStdString(name)).toStdString(); |
3734 | + |
3735 | + if (is_folder) |
3736 | + { |
3737 | + item_id += '/'; |
3738 | + } |
3739 | + |
3740 | + return item_id; |
3741 | +} |
3742 | + |
3743 | +bool is_folder(string const& item_id) |
3744 | +{ |
3745 | + auto size = item_id.size(); |
3746 | + if (size == 0) |
3747 | + { |
3748 | + throw InvalidArgumentException("Invalid blank item ID"); |
3749 | + } |
3750 | + if (size == 1 && item_id[0] == '.') |
3751 | + { |
3752 | + return true; |
3753 | + } |
3754 | + return item_id[size-1] == '/'; |
3755 | +} |
3756 | |
3757 | === modified file 'src/item_id.h' |
3758 | --- src/item_id.h 2016-09-18 10:00:45 +0000 |
3759 | +++ src/item_id.h 2016-11-25 05:01:23 +0000 |
3760 | @@ -1,3 +1,21 @@ |
3761 | +/* |
3762 | + * Copyright (C) 2016 Canonical Ltd. |
3763 | + * |
3764 | + * This program is free software: you can redistribute it and/or modify |
3765 | + * it under the terms of the GNU General Public License version 3 as |
3766 | + * published by the Free Software Foundation. |
3767 | + * |
3768 | + * This program is distributed in the hope that it will be useful, |
3769 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3770 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3771 | + * GNU General Public License for more details. |
3772 | + * |
3773 | + * You should have received a copy of the GNU General Public License |
3774 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3775 | + * |
3776 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3777 | + */ |
3778 | + |
3779 | #pragma once |
3780 | |
3781 | #include <QUrl> |
3782 | @@ -6,3 +24,8 @@ |
3783 | |
3784 | QUrl id_to_url(std::string const& item_id, QUrl const& base_url); |
3785 | std::string url_to_id(QUrl const& item_url, QUrl const& base_url); |
3786 | + |
3787 | +std::string make_child_id(std::string const& parent_id, std::string const& name, |
3788 | + bool is_folder=false); |
3789 | + |
3790 | +bool is_folder(std::string const& item_id); |
3791 | |
3792 | === modified file 'src/main.cpp' |
3793 | --- src/main.cpp 2016-09-22 06:40:07 +0000 |
3794 | +++ src/main.cpp 2016-11-25 05:01:23 +0000 |
3795 | @@ -1,3 +1,21 @@ |
3796 | +/* |
3797 | + * Copyright (C) 2016 Canonical Ltd. |
3798 | + * |
3799 | + * This program is free software: you can redistribute it and/or modify |
3800 | + * it under the terms of the GNU General Public License version 3 as |
3801 | + * published by the Free Software Foundation. |
3802 | + * |
3803 | + * This program is distributed in the hope that it will be useful, |
3804 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3805 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3806 | + * GNU General Public License for more details. |
3807 | + * |
3808 | + * You should have received a copy of the GNU General Public License |
3809 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3810 | + * |
3811 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3812 | + */ |
3813 | + |
3814 | #include <unity/storage/provider/Server.h> |
3815 | |
3816 | #include "OwncloudProvider.h" |
3817 | @@ -8,7 +26,7 @@ |
3818 | int main(int argc, char **argv) |
3819 | { |
3820 | string const bus_name = "com.canonical.StorageFramework.Provider.OwnCloud"; |
3821 | - string const account_service_id = "google-drive-scope"; //"storage-provider-owncloud"; |
3822 | + string const account_service_id = "storage-provider-owncloud"; |
3823 | |
3824 | Server<OwncloudProvider> server(bus_name, account_service_id); |
3825 | server.init(argc, argv); |
3826 | |
3827 | === modified file 'tests/CMakeLists.txt' |
3828 | --- tests/CMakeLists.txt 2016-09-23 03:28:37 +0000 |
3829 | +++ tests/CMakeLists.txt 2016-11-25 05:01:23 +0000 |
3830 | @@ -1,10 +1,4 @@ |
3831 | -# We add -g so we get debug info for the gtest stack frames with gdb. |
3832 | -# The warnings are suppressed so we get a noise-free build for gtest and gmock. |
3833 | - |
3834 | -set(old_cxx_flags ${CMAKE_CXX_FLAGS}) |
3835 | -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wno-old-style-cast -Wno-missing-field-initializers") |
3836 | find_package(GMock) |
3837 | -set(CMAKE_CXX_FLAGS ${old_cxx_flags}) |
3838 | |
3839 | configure_file(testsetup.h.in testsetup.h @ONLY) |
3840 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) |
3841 | @@ -15,6 +9,7 @@ |
3842 | multistatus |
3843 | item_id |
3844 | davprovider |
3845 | + http_error |
3846 | ) |
3847 | |
3848 | set(UNIT_TEST_TARGETS "") |
3849 | |
3850 | === modified file 'tests/davprovider/davprovider_test.cpp' |
3851 | --- tests/davprovider/davprovider_test.cpp 2016-09-27 06:28:45 +0000 |
3852 | +++ tests/davprovider/davprovider_test.cpp 2016-11-25 05:01:23 +0000 |
3853 | @@ -1,26 +1,74 @@ |
3854 | +/* |
3855 | + * Copyright (C) 2016 Canonical Ltd. |
3856 | + * |
3857 | + * This program is free software: you can redistribute it and/or modify |
3858 | + * it under the terms of the GNU General Public License version 3 as |
3859 | + * published by the Free Software Foundation. |
3860 | + * |
3861 | + * This program is distributed in the hope that it will be useful, |
3862 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3863 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3864 | + * GNU General Public License for more details. |
3865 | + * |
3866 | + * You should have received a copy of the GNU General Public License |
3867 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3868 | + * |
3869 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
3870 | + */ |
3871 | + |
3872 | #include "../../src/DavProvider.h" |
3873 | #include <utils/DBusEnvironment.h> |
3874 | #include <utils/DavEnvironment.h> |
3875 | #include <utils/ProviderEnvironment.h> |
3876 | #include <testsetup.h> |
3877 | |
3878 | +#include <gtest/gtest.h> |
3879 | #include <QCoreApplication> |
3880 | -#include <QFutureWatcher> |
3881 | #include <QNetworkAccessManager> |
3882 | #include <QNetworkRequest> |
3883 | #include <QSignalSpy> |
3884 | #include <QTemporaryDir> |
3885 | -#include <unity/storage/qt/client/Account.h> |
3886 | -#include <unity/storage/qt/client/Root.h> |
3887 | +#include <QTimer> |
3888 | +#include <unity/storage/qt/Account.h> |
3889 | +#include <unity/storage/qt/Downloader.h> |
3890 | +#include <unity/storage/qt/Item.h> |
3891 | +#include <unity/storage/qt/ItemJob.h> |
3892 | +#include <unity/storage/qt/ItemListJob.h> |
3893 | +#include <unity/storage/qt/StorageError.h> |
3894 | +#include <unity/storage/qt/Uploader.h> |
3895 | +#include <unity/storage/qt/VoidJob.h> |
3896 | |
3897 | -#include <gtest/gtest.h> |
3898 | +#include <fcntl.h> |
3899 | +#include <sys/stat.h> |
3900 | +#include <sys/types.h> |
3901 | +#include <unistd.h> |
3902 | +#include <utime.h> |
3903 | +#include <algorithm> |
3904 | |
3905 | using namespace std; |
3906 | -using namespace unity::storage::provider; |
3907 | -using namespace unity::storage::qt::client; |
3908 | -using unity::storage::ItemType; |
3909 | - |
3910 | -static constexpr int SIGNAL_WAIT_TIME = 30000; |
3911 | +using namespace unity::storage::qt; |
3912 | +namespace provider = unity::storage::provider; |
3913 | + |
3914 | +void PrintTo(QString const& str, std::ostream* os) |
3915 | +{ |
3916 | + *os << "QString(\"" << str.toStdString() << "\")"; |
3917 | +} |
3918 | + |
3919 | +namespace |
3920 | +{ |
3921 | + |
3922 | +const string file_contents = |
3923 | + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " |
3924 | + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " |
3925 | + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " |
3926 | + "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor " |
3927 | + "in reprehenderit in voluptate velit esse cillum dolore eu fugiat " |
3928 | + "nulla pariatur. Excepteur sint occaecat cupidatat non proident, " |
3929 | + "sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; |
3930 | + |
3931 | +constexpr int SIGNAL_WAIT_TIME = 30000; |
3932 | + |
3933 | +} |
3934 | |
3935 | class TestDavProvider : public DavProvider |
3936 | { |
3937 | @@ -30,7 +78,7 @@ |
3938 | { |
3939 | } |
3940 | |
3941 | - QUrl base_url(Context const& ctx) const override |
3942 | + QUrl base_url(provider::Context const& ctx) const override |
3943 | { |
3944 | Q_UNUSED(ctx); |
3945 | return base_url_; |
3946 | @@ -38,10 +86,11 @@ |
3947 | |
3948 | QNetworkReply *send_request( |
3949 | QNetworkRequest& request, QByteArray const& verb, QIODevice* data, |
3950 | - unity::storage::provider::Context const& ctx) const override |
3951 | + provider::Context const& ctx) const override |
3952 | { |
3953 | - const auto& creds = boost::get<PasswordCredentials>(ctx.credentials); |
3954 | - const auto credentials = QByteArray::fromStdString(creds.username + ":" + creds.password);; |
3955 | + const auto& creds = boost::get<provider::PasswordCredentials>(ctx.credentials); |
3956 | + const auto credentials = QByteArray::fromStdString(creds.username + ":" + |
3957 | + creds.password); |
3958 | request.setRawHeader(QByteArrayLiteral("Authorization"), |
3959 | QByteArrayLiteral("Basic ") + credentials.toBase64()); |
3960 | return network_->sendCustomRequest(request, verb, data); |
3961 | @@ -64,8 +113,8 @@ |
3962 | |
3963 | dav_env_.reset(new DavEnvironment(tmp_dir_->path())); |
3964 | provider_env_.reset(new ProviderEnvironment( |
3965 | - unique_ptr<ProviderBase>(new TestDavProvider(dav_env_->base_url())), |
3966 | - 3, *dbus_env_)); |
3967 | + make_shared<TestDavProvider>(dav_env_->base_url()), |
3968 | + 1, *dbus_env_)); |
3969 | } |
3970 | |
3971 | void TearDown() override |
3972 | @@ -76,11 +125,47 @@ |
3973 | dbus_env_.reset(); |
3974 | } |
3975 | |
3976 | - shared_ptr<Account> get_client() const |
3977 | + Account get_client() const |
3978 | { |
3979 | return provider_env_->get_client(); |
3980 | } |
3981 | |
3982 | + string local_file(string const& path) |
3983 | + { |
3984 | + return tmp_dir_->path().toStdString() + "/" + path; |
3985 | + } |
3986 | + |
3987 | + void make_file(string const& path) |
3988 | + { |
3989 | + string full_path = local_file(path); |
3990 | + int fd = open(full_path.c_str(), O_CREAT | O_EXCL, 0644); |
3991 | + ASSERT_GT(fd, 0); |
3992 | + ASSERT_EQ(0, close(fd)); |
3993 | + } |
3994 | + |
3995 | + void make_dir(string const& path) |
3996 | + { |
3997 | + string full_path = local_file(path); |
3998 | + ASSERT_EQ(0, mkdir(full_path.c_str(), 0755)); |
3999 | + } |
4000 | + |
4001 | + void touch_file(string const& path) |
4002 | + { |
4003 | + string full_path = local_file(path); |
4004 | + ASSERT_EQ(0, utime(full_path.c_str(), nullptr)); |
4005 | + } |
4006 | + |
4007 | + void offset_mtime(string const& path, int offset_seconds) |
4008 | + { |
4009 | + string full_path = local_file(path); |
4010 | + struct stat buf; |
4011 | + ASSERT_EQ(0, stat(full_path.c_str(), &buf)); |
4012 | + struct utimbuf times; |
4013 | + times.actime = buf.st_atime + offset_seconds; |
4014 | + times.modtime = buf.st_mtime + offset_seconds; |
4015 | + ASSERT_EQ(0, utime(full_path.c_str(), ×)); |
4016 | + } |
4017 | + |
4018 | private: |
4019 | std::unique_ptr<DBusEnvironment> dbus_env_; |
4020 | std::unique_ptr<QTemporaryDir> tmp_dir_; |
4021 | @@ -88,27 +173,753 @@ |
4022 | std::unique_ptr<ProviderEnvironment> provider_env_; |
4023 | }; |
4024 | |
4025 | +namespace |
4026 | +{ |
4027 | + |
4028 | +template <typename Job> |
4029 | +void wait_for(Job* job) |
4030 | +{ |
4031 | + QSignalSpy spy(job, &Job::statusChanged); |
4032 | + while (job->status() == Job::Loading) |
4033 | + { |
4034 | + if (!spy.wait(SIGNAL_WAIT_TIME)) |
4035 | + { |
4036 | + throw runtime_error("Wait for statusChanged signal timed out"); |
4037 | + } |
4038 | + } |
4039 | +} |
4040 | + |
4041 | +QList<Item> get_items(ItemListJob *job) |
4042 | +{ |
4043 | + QList<Item> items; |
4044 | + auto connection = QObject::connect( |
4045 | + job, &ItemListJob::itemsReady, |
4046 | + [&](QList<Item> const& new_items) |
4047 | + { |
4048 | + items.append(new_items); |
4049 | + }); |
4050 | + try |
4051 | + { |
4052 | + wait_for(job); |
4053 | + } |
4054 | + catch (...) |
4055 | + { |
4056 | + QObject::disconnect(connection); |
4057 | + throw; |
4058 | + } |
4059 | + QObject::disconnect(connection); |
4060 | + return items; |
4061 | +} |
4062 | + |
4063 | +Item get_root(Account const& account) |
4064 | +{ |
4065 | + unique_ptr<ItemListJob> job(account.roots()); |
4066 | + QList<Item> roots = get_items(job.get()); |
4067 | + if (job->status() != ItemListJob::Finished) |
4068 | + { |
4069 | + throw runtime_error("Account.roots(): " + |
4070 | + job->error().errorString().toStdString()); |
4071 | + } |
4072 | + return roots.at(0); |
4073 | +} |
4074 | + |
4075 | +} |
4076 | + |
4077 | TEST_F(DavProviderTests, roots) |
4078 | { |
4079 | auto account = get_client(); |
4080 | - auto future = account->roots(); |
4081 | - |
4082 | - QFutureWatcher<QVector<shared_ptr<Root>>> watcher; |
4083 | - QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
4084 | - watcher.setFuture(account->roots()); |
4085 | - if (spy.count() == 0) |
4086 | - { |
4087 | - ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4088 | - } |
4089 | - |
4090 | - auto roots = watcher.result(); |
4091 | + |
4092 | + unique_ptr<ItemListJob> job(account.roots()); |
4093 | + QList<Item> roots = get_items(job.get()); |
4094 | + ASSERT_EQ(ItemListJob::Finished, job->status()) |
4095 | + << job->error().errorString().toStdString(); |
4096 | + |
4097 | ASSERT_EQ(1, roots.size()); |
4098 | auto item = roots[0]; |
4099 | - EXPECT_EQ(".", item->native_identity()); |
4100 | - EXPECT_EQ("Root", item->name()); |
4101 | - EXPECT_EQ(ItemType::root, item->type()); |
4102 | - EXPECT_TRUE(item->parent_ids().isEmpty()); |
4103 | - EXPECT_TRUE(item->last_modified_time().isValid()); |
4104 | + EXPECT_EQ(".", item.itemId()); |
4105 | + EXPECT_EQ("Root", item.name()); |
4106 | + EXPECT_EQ(Item::Root, item.type()); |
4107 | + EXPECT_TRUE(item.parentIds().isEmpty()); |
4108 | + EXPECT_TRUE(item.lastModifiedTime().isValid()); |
4109 | +} |
4110 | + |
4111 | +TEST_F(DavProviderTests, list) |
4112 | +{ |
4113 | + auto account = get_client(); |
4114 | + make_file("foo.txt"); |
4115 | + make_file("bar.txt"); |
4116 | + make_dir("folder"); |
4117 | + make_file("I\u00F1t\u00EBrn\u00E2ti\u00F4n\u00E0liz\u00E6ti\u00F8n"); |
4118 | + |
4119 | + Item root = get_root(account); |
4120 | + |
4121 | + unique_ptr<ItemListJob> job(root.list()); |
4122 | + QList<Item> items = get_items(job.get()); |
4123 | + ASSERT_EQ(ItemListJob::Finished, job->status()) |
4124 | + << job->error().errorString().toStdString(); |
4125 | + |
4126 | + ASSERT_EQ(4, items.size()); |
4127 | + sort(items.begin(), items.end(), |
4128 | + [](Item const& a, Item const& b) -> bool { |
4129 | + return a.itemId() < b.itemId(); |
4130 | + }); |
4131 | + |
4132 | + EXPECT_EQ("I%C3%B1t%C3%ABrn%C3%A2ti%C3%B4n%C3%A0liz%C3%A6ti%C3%B8n", items[0].itemId()); |
4133 | + EXPECT_EQ(".", items[0].parentIds().at(0)); |
4134 | + EXPECT_EQ("I\u00F1t\u00EBrn\u00E2ti\u00F4n\u00E0liz\u00E6ti\u00F8n", items[0].name()); |
4135 | + EXPECT_EQ(Item::File, items[0].type()); |
4136 | + |
4137 | + EXPECT_EQ("bar.txt", items[1].itemId()); |
4138 | + EXPECT_EQ(".", items[1].parentIds().at(0)); |
4139 | + EXPECT_EQ("bar.txt", items[1].name()); |
4140 | + EXPECT_EQ(Item::File, items[1].type()); |
4141 | + |
4142 | + EXPECT_EQ("folder/", items[2].itemId()); |
4143 | + EXPECT_EQ(".", items[2].parentIds().at(0)); |
4144 | + EXPECT_EQ("folder", items[2].name()); |
4145 | + EXPECT_EQ(Item::Folder, items[2].type()); |
4146 | + |
4147 | + EXPECT_EQ("foo.txt", items[3].itemId()); |
4148 | + EXPECT_EQ(".", items[3].parentIds().at(0)); |
4149 | + EXPECT_EQ("foo.txt", items[3].name()); |
4150 | + EXPECT_EQ(Item::File, items[3].type()); |
4151 | +} |
4152 | + |
4153 | +TEST_F(DavProviderTests, lookup) |
4154 | +{ |
4155 | + auto account = get_client(); |
4156 | + make_file("foo.txt"); |
4157 | + |
4158 | + Item root = get_root(account); |
4159 | + |
4160 | + unique_ptr<ItemListJob> job(root.lookup("foo.txt")); |
4161 | + QList<Item> items = get_items(job.get()); |
4162 | + ASSERT_EQ(ItemListJob::Finished, job->status()) |
4163 | + << job->error().errorString().toStdString(); |
4164 | + |
4165 | + ASSERT_EQ(1, items.size()); |
4166 | + EXPECT_EQ("foo.txt", items[0].itemId()); |
4167 | + EXPECT_EQ(".", items[0].parentIds().at(0)); |
4168 | + EXPECT_EQ("foo.txt", items[0].name()); |
4169 | + EXPECT_EQ(Item::File, items[0].type()); |
4170 | +} |
4171 | + |
4172 | +TEST_F(DavProviderTests, metadata) |
4173 | +{ |
4174 | + auto account = get_client(); |
4175 | + make_file("foo.txt"); |
4176 | + |
4177 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4178 | + wait_for(job.get()); |
4179 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4180 | + << job->error().errorString().toStdString(); |
4181 | + |
4182 | + Item item = job->item(); |
4183 | + EXPECT_EQ("foo.txt", item.itemId()); |
4184 | + EXPECT_EQ(".", item.parentIds().at(0)); |
4185 | + EXPECT_EQ("foo.txt", item.name()); |
4186 | + EXPECT_EQ(Item::File, item.type()); |
4187 | +} |
4188 | + |
4189 | +TEST_F(DavProviderTests, metadata_not_found) |
4190 | +{ |
4191 | + auto account = get_client(); |
4192 | + |
4193 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4194 | + wait_for(job.get()); |
4195 | + ASSERT_EQ(ItemJob::Error, job->status()); |
4196 | + |
4197 | + auto error = job->error(); |
4198 | + EXPECT_EQ(StorageError::NotExists, error.type()); |
4199 | + EXPECT_TRUE(error.message().startsWith("Sabre\\DAV\\Exception\\NotFound: ")) |
4200 | + << error.message().toStdString(); |
4201 | +} |
4202 | + |
4203 | +TEST_F(DavProviderTests, create_folder) |
4204 | +{ |
4205 | + auto account = get_client(); |
4206 | + |
4207 | + Item root = get_root(account); |
4208 | + unique_ptr<ItemJob> job(root.createFolder("folder")); |
4209 | + wait_for(job.get()); |
4210 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4211 | + << job->error().errorString().toStdString(); |
4212 | + |
4213 | + Item folder = job->item(); |
4214 | + EXPECT_EQ("folder/", folder.itemId()); |
4215 | + EXPECT_EQ(".", folder.parentIds().at(0)); |
4216 | + EXPECT_EQ("folder", folder.name()); |
4217 | + EXPECT_EQ(Item::Folder, folder.type()); |
4218 | +} |
4219 | + |
4220 | +TEST_F(DavProviderTests, create_folder_reserved_chars) |
4221 | +{ |
4222 | + auto account = get_client(); |
4223 | + |
4224 | + Item root = get_root(account); |
4225 | + unique_ptr<ItemJob> job(root.createFolder("14:19")); |
4226 | + wait_for(job.get()); |
4227 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4228 | + << job->error().errorString().toStdString(); |
4229 | + |
4230 | + Item folder = job->item(); |
4231 | + EXPECT_EQ("14:19/", folder.itemId()); |
4232 | + EXPECT_EQ(".", folder.parentIds().at(0)); |
4233 | + EXPECT_EQ("14:19", folder.name()); |
4234 | + EXPECT_EQ(Item::Folder, folder.type()); |
4235 | +} |
4236 | + |
4237 | +TEST_F(DavProviderTests, create_folder_overwrite_file) |
4238 | +{ |
4239 | + auto account = get_client(); |
4240 | + make_file("folder"); |
4241 | + |
4242 | + Item root = get_root(account); |
4243 | + unique_ptr<ItemJob> job(root.createFolder("folder")); |
4244 | + wait_for(job.get()); |
4245 | + ASSERT_EQ(ItemJob::Error, job->status()); |
4246 | + |
4247 | + auto error = job->error(); |
4248 | + EXPECT_EQ(StorageError::Exists, error.type()); |
4249 | + EXPECT_EQ("Sabre\\DAV\\Exception\\MethodNotAllowed: The resource you tried to create already exists", error.message()); |
4250 | +} |
4251 | + |
4252 | +TEST_F(DavProviderTests, create_folder_overwrite_folder) |
4253 | +{ |
4254 | + auto account = get_client(); |
4255 | + ASSERT_EQ(0, mkdir(local_file("folder").c_str(), 0755)); |
4256 | + |
4257 | + Item root = get_root(account); |
4258 | + unique_ptr<ItemJob> job(root.createFolder("folder")); |
4259 | + wait_for(job.get()); |
4260 | + ASSERT_EQ(ItemJob::Error, job->status()); |
4261 | + |
4262 | + auto error = job->error(); |
4263 | + EXPECT_EQ(StorageError::Exists, error.type()); |
4264 | + EXPECT_EQ("Sabre\\DAV\\Exception\\MethodNotAllowed: The resource you tried to create already exists", error.message()); |
4265 | +} |
4266 | + |
4267 | +TEST_F(DavProviderTests, create_file) |
4268 | +{ |
4269 | + int const segments = 50; |
4270 | + |
4271 | + auto account = get_client(); |
4272 | + Item root = get_root(account); |
4273 | + |
4274 | + unique_ptr<Uploader> uploader( |
4275 | + root.createFile("filename.txt", Item::ErrorIfConflict, |
4276 | + file_contents.size() * segments, "text/plain")); |
4277 | + |
4278 | + int count = 0; |
4279 | + QTimer timer; |
4280 | + timer.setSingleShot(false); |
4281 | + timer.setInterval(10); |
4282 | + QObject::connect(&timer, &QTimer::timeout, [&] { |
4283 | + uploader->write(&file_contents[0], file_contents.size()); |
4284 | + count++; |
4285 | + if (count == segments) |
4286 | + { |
4287 | + uploader->close(); |
4288 | + } |
4289 | + }); |
4290 | + |
4291 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4292 | + timer.start(); |
4293 | + while (uploader->status() == Uploader::Loading || |
4294 | + uploader->status() == Uploader::Ready) |
4295 | + { |
4296 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4297 | + } |
4298 | + ASSERT_EQ(Uploader::Finished, uploader->status()) |
4299 | + << uploader->error().errorString().toStdString(); |
4300 | + |
4301 | + auto file = uploader->item(); |
4302 | + EXPECT_EQ("filename.txt", file.itemId()); |
4303 | + ASSERT_EQ(1, file.parentIds().size()); |
4304 | + EXPECT_EQ(".", file.parentIds().at(0)); |
4305 | + EXPECT_EQ("filename.txt", file.name()); |
4306 | + EXPECT_NE(0, file.etag().size()); |
4307 | + EXPECT_EQ(Item::File, file.type()); |
4308 | + EXPECT_EQ(int64_t(file_contents.size() * segments), file.sizeInBytes()); |
4309 | + |
4310 | + string full_path = local_file("filename.txt"); |
4311 | + struct stat buf; |
4312 | + ASSERT_EQ(0, stat(full_path.c_str(), &buf)); |
4313 | + EXPECT_EQ(off_t(file_contents.size() * segments), buf.st_size); |
4314 | +} |
4315 | + |
4316 | +TEST_F(DavProviderTests, create_file_over_existing_file) |
4317 | +{ |
4318 | + auto account = get_client(); |
4319 | + make_file("foo.txt"); |
4320 | + |
4321 | + Item root = get_root(account); |
4322 | + unique_ptr<Uploader> uploader( |
4323 | + root.createFile("foo.txt", Item::ErrorIfConflict, 0, "text/plain")); |
4324 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4325 | + while (uploader->status() == Uploader::Loading) |
4326 | + { |
4327 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4328 | + } |
4329 | + |
4330 | + uploader->close(); |
4331 | + while (uploader->status() == Uploader::Ready) |
4332 | + { |
4333 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4334 | + } |
4335 | + ASSERT_EQ(Uploader::Error, uploader->status()); |
4336 | + |
4337 | + auto error = uploader->error(); |
4338 | + EXPECT_EQ(StorageError::Conflict, error.type()); |
4339 | + EXPECT_EQ("Sabre\\DAV\\Exception\\PreconditionFailed: An If-None-Match header was specified, but the ETag matched (or * was specified).", error.message()); |
4340 | +} |
4341 | + |
4342 | +TEST_F(DavProviderTests, create_file_overwrite_existing) |
4343 | +{ |
4344 | + auto account = get_client(); |
4345 | + make_file("foo.txt"); |
4346 | + |
4347 | + Item root = get_root(account); |
4348 | + unique_ptr<Uploader> uploader( |
4349 | + root.createFile("foo.txt", Item::IgnoreConflict, 0, "text/plain")); |
4350 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4351 | + while (uploader->status() == Uploader::Loading) |
4352 | + { |
4353 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4354 | + } |
4355 | + |
4356 | + uploader->close(); |
4357 | + while (uploader->status() == Uploader::Ready) |
4358 | + { |
4359 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4360 | + } |
4361 | + ASSERT_EQ(Uploader::Finished, uploader->status()) |
4362 | + << uploader->error().errorString().toStdString(); |
4363 | +} |
4364 | + |
4365 | +TEST_F(DavProviderTests, update) |
4366 | +{ |
4367 | + int const segments = 50; |
4368 | + |
4369 | + auto account = get_client(); |
4370 | + make_file("foo.txt"); |
4371 | + // Offset to ensure modification time changes |
4372 | + offset_mtime("foo.txt", -10); |
4373 | + |
4374 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4375 | + wait_for(job.get()); |
4376 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4377 | + << job->error().errorString().toStdString(); |
4378 | + |
4379 | + auto file = job->item(); |
4380 | + QString old_etag = file.etag(); |
4381 | + |
4382 | + unique_ptr<Uploader> uploader( |
4383 | + file.createUploader(Item::ErrorIfConflict, |
4384 | + file_contents.size() * segments)); |
4385 | + |
4386 | + int count = 0; |
4387 | + QTimer timer; |
4388 | + timer.setSingleShot(false); |
4389 | + timer.setInterval(10); |
4390 | + QObject::connect(&timer, &QTimer::timeout, [&] { |
4391 | + uploader->write(&file_contents[0], file_contents.size()); |
4392 | + count++; |
4393 | + if (count == segments) |
4394 | + { |
4395 | + uploader->close(); |
4396 | + } |
4397 | + }); |
4398 | + |
4399 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4400 | + timer.start(); |
4401 | + while (uploader->status() == Uploader::Loading || |
4402 | + uploader->status() == Uploader::Ready) |
4403 | + { |
4404 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4405 | + } |
4406 | + ASSERT_EQ(Uploader::Finished, uploader->status()) |
4407 | + << uploader->error().errorString().toStdString(); |
4408 | + |
4409 | + file = uploader->item(); |
4410 | + EXPECT_NE(old_etag, file.etag()); |
4411 | + EXPECT_EQ(int64_t(file_contents.size() * segments), file.sizeInBytes()); |
4412 | +} |
4413 | + |
4414 | +TEST_F(DavProviderTests, update_conflict) |
4415 | +{ |
4416 | + auto account = get_client(); |
4417 | + make_file("foo.txt"); |
4418 | + // Offset to ensure modification time changes |
4419 | + offset_mtime("foo.txt", -10); |
4420 | + |
4421 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4422 | + wait_for(job.get()); |
4423 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4424 | + << job->error().errorString().toStdString(); |
4425 | + |
4426 | + // Change the file after metadata has been looked up |
4427 | + touch_file("foo.txt"); |
4428 | + |
4429 | + auto file = job->item(); |
4430 | + unique_ptr<Uploader> uploader( |
4431 | + file.createUploader(Item::ErrorIfConflict, 0)); |
4432 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4433 | + while (uploader->status() == Uploader::Loading) |
4434 | + { |
4435 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4436 | + } |
4437 | + |
4438 | + uploader->close(); |
4439 | + while (uploader->status() == Uploader::Ready) |
4440 | + { |
4441 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4442 | + } |
4443 | + ASSERT_EQ(Uploader::Error, uploader->status()); |
4444 | + |
4445 | + auto error = uploader->error(); |
4446 | + EXPECT_EQ(StorageError::Conflict, error.type()); |
4447 | + EXPECT_EQ("Sabre\\DAV\\Exception\\PreconditionFailed: An If-Match header was specified, but none of the specified the ETags matched.", error.message()); |
4448 | +} |
4449 | + |
4450 | +TEST_F(DavProviderTests, upload_short_write) |
4451 | +{ |
4452 | + auto account = get_client(); |
4453 | + |
4454 | + Item root = get_root(account); |
4455 | + unique_ptr<Uploader> uploader( |
4456 | + root.createFile("foo.txt", Item::ErrorIfConflict, 1000, "text/plain")); |
4457 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4458 | + while (uploader->status() == Uploader::Loading) |
4459 | + { |
4460 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4461 | + } |
4462 | + |
4463 | + uploader->close(); |
4464 | + while (uploader->status() == Uploader::Ready) |
4465 | + { |
4466 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4467 | + } |
4468 | + ASSERT_EQ(Uploader::Error, uploader->status()); |
4469 | + |
4470 | + auto error = uploader->error(); |
4471 | + EXPECT_EQ(StorageError::RemoteCommsError, error.type()); |
4472 | + EXPECT_EQ("Unknown error", error.message()); |
4473 | +} |
4474 | + |
4475 | +TEST_F(DavProviderTests, upload_cancel) |
4476 | +{ |
4477 | + auto account = get_client(); |
4478 | + |
4479 | + Item root = get_root(account); |
4480 | + unique_ptr<Uploader> uploader( |
4481 | + root.createFile("foo.txt", Item::ErrorIfConflict, 1000, "text/plain")); |
4482 | + QSignalSpy spy(uploader.get(), &Uploader::statusChanged); |
4483 | + while (uploader->status() == Uploader::Loading) |
4484 | + { |
4485 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4486 | + } |
4487 | + |
4488 | + uploader->cancel(); |
4489 | + while (uploader->status() == Uploader::Ready) |
4490 | + { |
4491 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4492 | + } |
4493 | + ASSERT_EQ(Uploader::Cancelled, uploader->status()); |
4494 | + |
4495 | + auto error = uploader->error(); |
4496 | + EXPECT_EQ(StorageError::Cancelled, error.type()); |
4497 | + EXPECT_EQ("Uploader::cancel(): upload was cancelled", error.message()); |
4498 | +} |
4499 | + |
4500 | +TEST_F(DavProviderTests, download) |
4501 | +{ |
4502 | + int const segments = 1000; |
4503 | + string large_contents; |
4504 | + large_contents.reserve(file_contents.size() * segments); |
4505 | + for (int i = 0; i < segments; i++) |
4506 | + { |
4507 | + large_contents += file_contents; |
4508 | + } |
4509 | + string const full_path = local_file("foo.txt"); |
4510 | + { |
4511 | + int fd = open(full_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644); |
4512 | + ASSERT_GT(fd, 0); |
4513 | + ASSERT_EQ(ssize_t(large_contents.size()), write(fd, &large_contents[0], large_contents.size())) << strerror(errno); |
4514 | + ASSERT_EQ(0, close(fd)); |
4515 | + } |
4516 | + |
4517 | + auto account = get_client(); |
4518 | + |
4519 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4520 | + wait_for(job.get()); |
4521 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4522 | + << job->error().errorString().toStdString(); |
4523 | + |
4524 | + auto file = job->item(); |
4525 | + |
4526 | + unique_ptr<Downloader> downloader( |
4527 | + file.createDownloader(Item::ErrorIfConflict)); |
4528 | + |
4529 | + int64_t n_read = 0; |
4530 | + QObject::connect(downloader.get(), &QIODevice::readyRead, |
4531 | + [&]() { |
4532 | + auto bytes = downloader->readAll(); |
4533 | + string const expected = large_contents.substr( |
4534 | + n_read, bytes.size()); |
4535 | + EXPECT_EQ(expected, bytes.toStdString()); |
4536 | + n_read += bytes.size(); |
4537 | + }); |
4538 | + QSignalSpy read_finished_spy( |
4539 | + downloader.get(), &QIODevice::readChannelFinished); |
4540 | + ASSERT_TRUE(read_finished_spy.wait(SIGNAL_WAIT_TIME)); |
4541 | + |
4542 | + QSignalSpy status_spy(downloader.get(), &Downloader::statusChanged); |
4543 | + downloader->close(); |
4544 | + while (downloader->status() == Downloader::Ready) |
4545 | + { |
4546 | + ASSERT_TRUE(status_spy.wait(SIGNAL_WAIT_TIME)); |
4547 | + } |
4548 | + ASSERT_EQ(Downloader::Finished, downloader->status()) |
4549 | + << downloader->error().errorString().toStdString(); |
4550 | + |
4551 | + EXPECT_EQ(int64_t(large_contents.size()), n_read); |
4552 | +} |
4553 | + |
4554 | +TEST_F(DavProviderTests, download_short_read) |
4555 | +{ |
4556 | + int const segments = 1000; |
4557 | + { |
4558 | + string full_path = local_file("foo.txt"); |
4559 | + int fd = open(full_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644); |
4560 | + ASSERT_GT(fd, 0); |
4561 | + for (int i = 0; i < segments; i++) |
4562 | + { |
4563 | + ASSERT_EQ(ssize_t(file_contents.size()), write(fd, &file_contents[0], file_contents.size())) << strerror(errno); |
4564 | + } |
4565 | + ASSERT_EQ(0, close(fd)); |
4566 | + } |
4567 | + |
4568 | + auto account = get_client(); |
4569 | + |
4570 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4571 | + wait_for(job.get()); |
4572 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4573 | + << job->error().errorString().toStdString(); |
4574 | + |
4575 | + auto file = job->item(); |
4576 | + unique_ptr<Downloader> downloader( |
4577 | + file.createDownloader(Item::ErrorIfConflict)); |
4578 | + |
4579 | + QSignalSpy spy(downloader.get(), &Downloader::statusChanged); |
4580 | + while (downloader->status() == Downloader::Loading) |
4581 | + { |
4582 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4583 | + } |
4584 | + |
4585 | + downloader->close(); |
4586 | + while (downloader->status() == Downloader::Ready) |
4587 | + { |
4588 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
4589 | + } |
4590 | + ASSERT_EQ(Downloader::Error, downloader->status()); |
4591 | + |
4592 | + auto error = downloader->error(); |
4593 | + EXPECT_EQ(StorageError::LogicError, error.type()); |
4594 | + EXPECT_EQ("finish called before all data sent", error.message()); |
4595 | +} |
4596 | + |
4597 | +TEST_F(DavProviderTests, download_not_found) |
4598 | +{ |
4599 | + auto account = get_client(); |
4600 | + make_file("foo.txt"); |
4601 | + |
4602 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4603 | + wait_for(job.get()); |
4604 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4605 | + << job->error().errorString().toStdString(); |
4606 | + |
4607 | + auto file = job->item(); |
4608 | + |
4609 | + ASSERT_EQ(0, unlink(local_file("foo.txt").c_str())); |
4610 | + |
4611 | + unique_ptr<Downloader> downloader( |
4612 | + file.createDownloader(Item::ErrorIfConflict)); |
4613 | + |
4614 | + QObject::connect(downloader.get(), &QIODevice::readyRead, |
4615 | + [&]() { |
4616 | + downloader->readAll(); |
4617 | + }); |
4618 | + QSignalSpy read_finished_spy( |
4619 | + downloader.get(), &QIODevice::readChannelFinished); |
4620 | + ASSERT_TRUE(read_finished_spy.wait(SIGNAL_WAIT_TIME)); |
4621 | + |
4622 | + QSignalSpy status_spy(downloader.get(), &Downloader::statusChanged); |
4623 | + downloader->close(); |
4624 | + while (downloader->status() == Downloader::Ready) |
4625 | + { |
4626 | + ASSERT_TRUE(status_spy.wait(SIGNAL_WAIT_TIME)); |
4627 | + } |
4628 | + ASSERT_EQ(Downloader::Error, downloader->status()); |
4629 | + |
4630 | + auto error = downloader->error(); |
4631 | + EXPECT_EQ(StorageError::NotExists, error.type()); |
4632 | + EXPECT_TRUE(error.message().startsWith("Sabre\\DAV\\Exception\\NotFound: ")) |
4633 | + << error.message().toStdString(); |
4634 | +} |
4635 | + |
4636 | +TEST_F(DavProviderTests, delete_item) |
4637 | +{ |
4638 | + auto account = get_client(); |
4639 | + make_file("foo.txt"); |
4640 | + |
4641 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4642 | + wait_for(job.get()); |
4643 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4644 | + << job->error().errorString().toStdString(); |
4645 | + |
4646 | + Item item = job->item(); |
4647 | + unique_ptr<VoidJob> delete_job(item.deleteItem()); |
4648 | + wait_for(delete_job.get()); |
4649 | + ASSERT_EQ(VoidJob::Finished, delete_job->status()) |
4650 | + << delete_job->error().errorString().toStdString(); |
4651 | + |
4652 | + struct stat buf; |
4653 | + EXPECT_EQ(-1, stat(local_file("foo.txt").c_str(), &buf)); |
4654 | + EXPECT_EQ(ENOENT, errno); |
4655 | +} |
4656 | + |
4657 | +TEST_F(DavProviderTests, delete_item_not_found) |
4658 | +{ |
4659 | + auto account = get_client(); |
4660 | + make_file("foo.txt"); |
4661 | + |
4662 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4663 | + wait_for(job.get()); |
4664 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4665 | + << job->error().errorString().toStdString(); |
4666 | + |
4667 | + Item item = job->item(); |
4668 | + |
4669 | + ASSERT_EQ(0, unlink(local_file("foo.txt").c_str())); |
4670 | + |
4671 | + unique_ptr<VoidJob> delete_job(item.deleteItem()); |
4672 | + wait_for(delete_job.get()); |
4673 | + ASSERT_EQ(VoidJob::Error, delete_job->status()); |
4674 | + |
4675 | + auto error = delete_job->error(); |
4676 | + EXPECT_EQ(StorageError::NotExists, error.type()); |
4677 | + EXPECT_TRUE(error.message().startsWith("Sabre\\DAV\\Exception\\NotFound: ")) |
4678 | + << error.message().toStdString(); |
4679 | +} |
4680 | + |
4681 | +TEST_F(DavProviderTests, move) |
4682 | +{ |
4683 | + string const full_path = local_file("foo.txt"); |
4684 | + { |
4685 | + int fd = open(full_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644); |
4686 | + ASSERT_GT(fd, 0); |
4687 | + ASSERT_EQ(ssize_t(file_contents.size()), write(fd, &file_contents[0], file_contents.size())) << strerror(errno); |
4688 | + ASSERT_EQ(0, close(fd)); |
4689 | + } |
4690 | + |
4691 | + auto account = get_client(); |
4692 | + Item root = get_root(account); |
4693 | + |
4694 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4695 | + wait_for(job.get()); |
4696 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4697 | + << job->error().errorString().toStdString(); |
4698 | + |
4699 | + Item item = job->item(); |
4700 | + |
4701 | + job.reset(item.move(root, "new-name.txt")); |
4702 | + wait_for(job.get()); |
4703 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4704 | + << job->error().errorString().toStdString(); |
4705 | + |
4706 | + item = job->item(); |
4707 | + EXPECT_EQ("new-name.txt", item.itemId()); |
4708 | + ASSERT_EQ(1, item.parentIds().size()); |
4709 | + EXPECT_EQ(".", item.parentIds().at(0)); |
4710 | + EXPECT_EQ("new-name.txt", item.name()); |
4711 | + EXPECT_NE(0, item.etag().size()); |
4712 | + EXPECT_EQ(Item::File, item.type()); |
4713 | + EXPECT_EQ(int64_t(file_contents.size()), item.sizeInBytes()); |
4714 | + |
4715 | + // The old file no longer exists |
4716 | + struct stat buf; |
4717 | + EXPECT_EQ(-1, stat(full_path.c_str(), &buf)); |
4718 | + EXPECT_EQ(ENOENT, errno); |
4719 | + |
4720 | + // And the new one does |
4721 | + string const new_path = local_file("new-name.txt"); |
4722 | + EXPECT_EQ(0, stat(new_path.c_str(), &buf)); |
4723 | + EXPECT_EQ(off_t(file_contents.size()), buf.st_size); |
4724 | + |
4725 | + // And its has the expected contents |
4726 | + { |
4727 | + int fd = open(new_path.c_str(), O_RDONLY); |
4728 | + ASSERT_GT(fd, 0); |
4729 | + string contents(file_contents.size(), '\0'); |
4730 | + EXPECT_EQ(ssize_t(file_contents.size()), read(fd, &contents[0], contents.size())); |
4731 | + EXPECT_EQ(0, close(fd)); |
4732 | + EXPECT_EQ(file_contents, contents); |
4733 | + } |
4734 | +} |
4735 | + |
4736 | +TEST_F(DavProviderTests, copy) |
4737 | +{ |
4738 | + string const full_path = local_file("foo.txt"); |
4739 | + { |
4740 | + int fd = open(full_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644); |
4741 | + ASSERT_GT(fd, 0); |
4742 | + ASSERT_EQ(ssize_t(file_contents.size()), write(fd, &file_contents[0], file_contents.size())) << strerror(errno); |
4743 | + ASSERT_EQ(0, close(fd)); |
4744 | + } |
4745 | + |
4746 | + auto account = get_client(); |
4747 | + Item root = get_root(account); |
4748 | + |
4749 | + unique_ptr<ItemJob> job(account.get("foo.txt")); |
4750 | + wait_for(job.get()); |
4751 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4752 | + << job->error().errorString().toStdString(); |
4753 | + |
4754 | + Item item = job->item(); |
4755 | + |
4756 | + job.reset(item.copy(root, "new-name.txt")); |
4757 | + wait_for(job.get()); |
4758 | + ASSERT_EQ(ItemJob::Finished, job->status()) |
4759 | + << job->error().errorString().toStdString(); |
4760 | + |
4761 | + item = job->item(); |
4762 | + EXPECT_EQ("new-name.txt", item.itemId()); |
4763 | + ASSERT_EQ(1, item.parentIds().size()); |
4764 | + EXPECT_EQ(".", item.parentIds().at(0)); |
4765 | + EXPECT_EQ("new-name.txt", item.name()); |
4766 | + EXPECT_NE(0, item.etag().size()); |
4767 | + EXPECT_EQ(Item::File, item.type()); |
4768 | + EXPECT_EQ(int64_t(file_contents.size()), item.sizeInBytes()); |
4769 | + |
4770 | + // The old file still exists |
4771 | + struct stat buf; |
4772 | + EXPECT_EQ(0, stat(full_path.c_str(), &buf)); |
4773 | + |
4774 | + // And the new one does too |
4775 | + string const new_path = local_file("new-name.txt"); |
4776 | + EXPECT_EQ(0, stat(new_path.c_str(), &buf)); |
4777 | + EXPECT_EQ(off_t(file_contents.size()), buf.st_size); |
4778 | + |
4779 | + // And its has the expected contents |
4780 | + { |
4781 | + int fd = open(new_path.c_str(), O_RDONLY); |
4782 | + ASSERT_GT(fd, 0); |
4783 | + string contents(file_contents.size(), '\0'); |
4784 | + EXPECT_EQ(ssize_t(file_contents.size()), read(fd, &contents[0], contents.size())); |
4785 | + EXPECT_EQ(0, close(fd)); |
4786 | + EXPECT_EQ(file_contents, contents); |
4787 | + } |
4788 | } |
4789 | |
4790 | int main(int argc, char**argv) |
4791 | |
4792 | === added directory 'tests/http_error' |
4793 | === added file 'tests/http_error/CMakeLists.txt' |
4794 | --- tests/http_error/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
4795 | +++ tests/http_error/CMakeLists.txt 2016-11-25 05:01:23 +0000 |
4796 | @@ -0,0 +1,8 @@ |
4797 | +add_executable(http_error_test http_error_test.cpp) |
4798 | +target_link_libraries(http_error_test |
4799 | + dav-provider-lib |
4800 | + testutils |
4801 | + Qt5::Test |
4802 | + gtest |
4803 | +) |
4804 | +add_test(http_error_test http_error_test) |
4805 | |
4806 | === added file 'tests/http_error/http_error_test.cpp' |
4807 | --- tests/http_error/http_error_test.cpp 1970-01-01 00:00:00 +0000 |
4808 | +++ tests/http_error/http_error_test.cpp 2016-11-25 05:01:23 +0000 |
4809 | @@ -0,0 +1,278 @@ |
4810 | +/* |
4811 | + * Copyright (C) 2016 Canonical Ltd. |
4812 | + * |
4813 | + * This program is free software: you can redistribute it and/or modify |
4814 | + * it under the terms of the GNU General Public License version 3 as |
4815 | + * published by the Free Software Foundation. |
4816 | + * |
4817 | + * This program is distributed in the hope that it will be useful, |
4818 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4819 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4820 | + * GNU General Public License for more details. |
4821 | + * |
4822 | + * You should have received a copy of the GNU General Public License |
4823 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4824 | + * |
4825 | + * Authored by: James Henstridge <james.henstridge@canonical.com> |
4826 | + */ |
4827 | + |
4828 | +#include "../../src/http_error.h" |
4829 | + |
4830 | +#include <gtest/gtest.h> |
4831 | +#include <QBuffer> |
4832 | +#include <QByteArray> |
4833 | +#include <QCoreApplication> |
4834 | +#include <QNetworkReply> |
4835 | +#include <QNetworkRequest> |
4836 | +#include <unity/storage/provider/Exceptions.h> |
4837 | + |
4838 | +using namespace std; |
4839 | +using namespace unity::storage::provider; |
4840 | + |
4841 | +class FakeReply : public QNetworkReply |
4842 | +{ |
4843 | +public: |
4844 | + FakeReply(QString const& method, int status, QString const& reason, |
4845 | + QString const& content_type, QByteArray const& body) |
4846 | + { |
4847 | + QNetworkRequest request; |
4848 | + request.setAttribute(QNetworkRequest::CustomVerbAttribute, method); |
4849 | + setRequest(request); |
4850 | + |
4851 | + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status); |
4852 | + setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reason); |
4853 | + |
4854 | + setHeader(QNetworkRequest::ContentTypeHeader, content_type); |
4855 | + setHeader(QNetworkRequest::ContentLengthHeader, body.size()); |
4856 | + buffer_.setData(body); |
4857 | + buffer_.open(QIODevice::ReadOnly); |
4858 | + open(QIODevice::ReadOnly); |
4859 | + setFinished(true); |
4860 | + } |
4861 | + |
4862 | + FakeReply(QNetworkReply::NetworkError code, QString const& message) |
4863 | + { |
4864 | + setError(code, message); |
4865 | + setFinished(true); |
4866 | + } |
4867 | + |
4868 | + void abort() override |
4869 | + { |
4870 | + close(); |
4871 | + } |
4872 | + |
4873 | + qint64 bytesAvailable() const override |
4874 | + { |
4875 | + return QNetworkReply::bytesAvailable() + buffer_.bytesAvailable(); |
4876 | + } |
4877 | + |
4878 | + bool isSequential() const override |
4879 | + { |
4880 | + return true; |
4881 | + } |
4882 | + |
4883 | + qint64 size() const override |
4884 | + { |
4885 | + return buffer_.size(); |
4886 | + } |
4887 | + |
4888 | + qint64 readData(char *data, qint64 length) override |
4889 | + { |
4890 | + return buffer_.read(data, length); |
4891 | + } |
4892 | + |
4893 | +private: |
4894 | + QBuffer buffer_; |
4895 | +}; |
4896 | + |
4897 | +template <typename E> |
4898 | +E expect_throw(boost::exception_ptr p) |
4899 | +{ |
4900 | + try |
4901 | + { |
4902 | + boost::rethrow_exception(p); |
4903 | + } |
4904 | + catch (E const& e) |
4905 | + { |
4906 | + return e; |
4907 | + } |
4908 | + throw std::runtime_error("No exception thrown"); |
4909 | +} |
4910 | + |
4911 | + |
4912 | +TEST(HttpErrorTests, network_error) |
4913 | +{ |
4914 | + FakeReply reply(QNetworkReply::HostNotFoundError, "Host not found"); |
4915 | + |
4916 | + auto e = expect_throw<RemoteCommsException>( |
4917 | + translate_http_error(&reply, QByteArray())); |
4918 | + EXPECT_EQ("Host not found", e.error_message()); |
4919 | +} |
4920 | + |
4921 | +TEST(HttpErrorTests, text_plain_body) |
4922 | +{ |
4923 | + FakeReply reply("GET", 400, "Bad Request", |
4924 | + "text/plain;charset=US-ASCII", "error message"); |
4925 | + auto e = expect_throw<RemoteCommsException>( |
4926 | + translate_http_error(&reply, QByteArray())); |
4927 | + EXPECT_EQ("error message", e.error_message()); |
4928 | +} |
4929 | + |
4930 | +TEST(HttpErrorTests, application_xml_body) |
4931 | +{ |
4932 | + static const char xml[] = R"(<?xml version="1.0" encoding="utf-8"?> |
4933 | +<d:error xmlns:d='DAV:' xmlns:s='http://sabredav.org/ns'> |
4934 | + <s:exception>exception_name</s:exception> |
4935 | + <s:message>error message</s:message> |
4936 | + <s:sabredav-version>1.8.12</s:sabredav-version> |
4937 | +</d:error> |
4938 | +)"; |
4939 | + FakeReply reply("GET", 400, "Bad Request", |
4940 | + "application/xml;charset=utf-8", xml); |
4941 | + |
4942 | + auto e = expect_throw<RemoteCommsException>( |
4943 | + translate_http_error(&reply, QByteArray())); |
4944 | + EXPECT_EQ("exception_name: error message", e.error_message()); |
4945 | +} |
4946 | + |
4947 | +TEST(HttpErrorTests, fallback_body) |
4948 | +{ |
4949 | + FakeReply reply("GET", 400, "Bad Request", |
4950 | + "application/octet-stream", "foo"); |
4951 | + auto e = expect_throw<RemoteCommsException>( |
4952 | + translate_http_error(&reply, QByteArray())); |
4953 | + EXPECT_EQ("Bad Request", e.error_message()); |
4954 | +} |
4955 | + |
4956 | +TEST(HttpErrorTests, body_provided_separately) |
4957 | +{ |
4958 | + static const char xml[] = R"(<?xml version="1.0" encoding="utf-8"?> |
4959 | +<d:error xmlns:d='DAV:' xmlns:s='http://sabredav.org/ns'> |
4960 | + <s:exception>exception_name</s:exception> |
4961 | + <s:message>error message</s:message> |
4962 | + <s:sabredav-version>1.8.12</s:sabredav-version> |
4963 | +</d:error> |
4964 | +)"; |
4965 | + FakeReply reply("GET", 400, "Bad Request", |
4966 | + "application/xml;charset=utf-8", "foo"); |
4967 | + |
4968 | + auto e = expect_throw<RemoteCommsException>( |
4969 | + translate_http_error(&reply, xml)); |
4970 | + EXPECT_EQ("exception_name: error message", e.error_message()); |
4971 | +} |
4972 | + |
4973 | +TEST(HttpErrorTests, 401) |
4974 | +{ |
4975 | + FakeReply reply("GET", 401, "Unauthorised", "text/plain", "message"); |
4976 | + |
4977 | + auto e = expect_throw<PermissionException>( |
4978 | + translate_http_error(&reply, QByteArray())); |
4979 | + EXPECT_EQ("message", e.error_message()); |
4980 | +} |
4981 | + |
4982 | +TEST(HttpErrorTests, 403) |
4983 | +{ |
4984 | + FakeReply reply("GET", 403, "Forbidden", "text/plain", "message"); |
4985 | + |
4986 | + auto e = expect_throw<PermissionException>( |
4987 | + translate_http_error(&reply, QByteArray())); |
4988 | + EXPECT_EQ("message", e.error_message()); |
4989 | +} |
4990 | + |
4991 | +TEST(HttpErrorTests, 451) |
4992 | +{ |
4993 | + FakeReply reply("GET", 451, "Unavailable for Legal Reasons", |
4994 | + "text/plain", "message"); |
4995 | + |
4996 | + auto e = expect_throw<PermissionException>( |
4997 | + translate_http_error(&reply, QByteArray())); |
4998 | + EXPECT_EQ("message", e.error_message()); |
4999 | +} |
5000 | + |
The diff has been truncated for viewing.
Yep!