GCC Code Coverage Report


Directory: src/
File: src/drive/msg-drive-service.c
Date: 2025-01-11 02:01:05
Exec Total Coverage
Lines: 312 404 77.2%
Functions: 20 21 95.2%
Branches: 87 171 50.9%

Line Branch Exec Source
1 /* Copyright 2023-2024 Jan-Michael Brummer
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <libsoup/soup.h>
18 #include <json-glib/json-glib.h>
19
20 #include "msg-authorizer.h"
21 #include "msg-error.h"
22 #include "msg-input-stream.h"
23 #include "msg-private.h"
24 #include "msg-service.h"
25 #include "drive/msg-drive.h"
26 #include "drive/msg-drive-item.h"
27 #include "drive/msg-drive-item-file.h"
28 #include "drive/msg-drive-service.h"
29
30 struct _MsgDriveService {
31 MsgService parent_instance;
32
33 MsgDriveType type;
34 };
35
36
6/7
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 66 times.
150 G_DEFINE_TYPE (MsgDriveService, msg_drive_service, MSG_TYPE_SERVICE);
37
38 static gboolean
39 16 is_valid_name (const char *name)
40 {
41 16 g_auto (GStrv) split = NULL;
42 16 const char *invalid_names[] = {
43 ".lock",
44 "CON",
45 "PRN",
46 "AUX",
47 "NUL",
48 "COM0",
49 "COM1",
50 "COM2",
51 "COM3",
52 "COM4",
53 "COM5",
54 "COM6",
55 "COM7",
56 "COM8",
57 "COM9",
58 "LPT0",
59 "LPT1",
60 "LPT2",
61 "LPT3",
62 "LPT4",
63 "LPT5",
64 "LPT6",
65 "LPT7",
66 "LPT8",
67 "LPT9",
68 "_vti_",
69 "desktop.ini",
70 NULL
71 };
72 32 g_autofree char *lower_name = g_utf8_strdown (name, -1);
73
74 /* Check for invalid names */
75
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 16 times.
448 for (int i = 0; invalid_names[i] != NULL; i++) {
76
1/2
✓ Branch 2 taken 432 times.
✗ Branch 3 not taken.
864 g_autofree char *invalid_name = g_utf8_strdown (invalid_names[i], -1);
77
78
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 432 times.
432 if (g_strcmp0 (lower_name, invalid_name) == 0)
79 return FALSE;
80 }
81
82 /* Leading or Trailing whitespaces are invalid */
83
8/16
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 16 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 16 times.
✓ Branch 14 taken 16 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 16 times.
16 if (g_str_has_prefix (name, " ") || g_str_has_suffix (name, " "))
84 return FALSE;
85
86 /* Must not start with ~$ */
87
4/8
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 16 times.
16 if (g_str_has_prefix (name, "~$"))
88 return FALSE;
89
90 /* _vti_ cannot be part of the name */
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (strstr (name, "_vti_"))
92 return FALSE;
93
94 /* Not allowed chars: " * : < > ? / \ | */
95
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (strchr (name, '"') ||
96
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, '*') ||
97
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, ':') ||
98
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, '<') ||
99
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, '>') ||
100
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, '?') ||
101
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, '/') ||
102
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 strchr (name, '\\') ||
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 strchr (name, '|'))
104 return FALSE;
105
106 /* "forms" on root level for a library */
107 16 split = g_strsplit (name, "/", -1);
108
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
16 if (g_strv_length (split) == 2 && g_strcmp0 (split[1], "forms") == 0)
109 return FALSE;
110
111 16 return TRUE;
112 }
113
114 static void
115 2 msg_drive_service_init (__attribute__ ((unused)) MsgDriveService *self)
116 {
117 2 }
118
119 static void
120 3 msg_drive_service_class_init (__attribute__ ((unused)) MsgDriveServiceClass *class)
121 {
122 3 }
123
124 /**
125 * msg_drive_service_new:
126 * @authorizer: A authorizer
127 *
128 * Creates a new `MsgDriveService` using #MsgAuthorizer.
129 *
130 * Returns: the newly created `MsgDriveService`
131 */
132 MsgDriveService *
133 2 msg_drive_service_new (MsgAuthorizer *authorizer)
134 {
135 2 return g_object_new (MSG_TYPE_DRIVE_SERVICE, "authorizer", authorizer, NULL);
136 }
137
138 /**
139 * msg_drive_service_get_drives:
140 * @self: a #MsgDriveService
141 * @cancellable: a #GCancellable
142 * @error: a #GError
143 *
144 * Queries the Microsoft Graph API for all the drives of the currently logged in user
145 *
146 * Returns: (element-type MsgDrive) (transfer full): all drives the user can access
147 */
148 GList *
149 10 msg_drive_service_get_drives (MsgDriveService *self,
150 GCancellable *cancellable,
151 GError **error)
152 {
153 10 g_autofree char *url = NULL;
154 10 JsonObject *root_object = NULL;
155 10 JsonArray *array = NULL;
156 10 guint array_length = 0, index = 0;
157 10 g_autolist (MsgDrive) list = NULL;
158 10 g_autoptr (GBytes) response = NULL;
159 10 g_autoptr (JsonParser) parser = NULL;
160
161
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
162 return NULL;
163
164 10 url = g_strconcat (MSG_API_ENDPOINT, "/me/drives", NULL);
165
166 do {
167
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 g_autoptr (SoupMessage) message = NULL;
168
169 10 message = msg_service_build_message (MSG_SERVICE (self), "GET", url, NULL, FALSE);
170 10 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!parser)
172 return NULL;
173
174 10 array = json_object_get_array_member (root_object, "value");
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 g_assert (array != NULL);
176
177 10 array_length = json_array_get_length (array);
178
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
20 for (index = 0; index < array_length; index++) {
179 10 MsgDrive *drive = NULL;
180 10 JsonObject *drive_object = NULL;
181 10 g_autoptr (GError) local_error = NULL;
182
183 10 drive_object = json_array_get_object_element (array, index);
184 10 drive = msg_drive_new_from_json (drive_object, &local_error);
185
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (drive) {
186 10 self->type = msg_drive_get_drive_type (drive);
187 10 list = g_list_append (list, drive);
188 } else {
189 g_warning ("Could not parse drive object: %s", local_error->message);
190 }
191 }
192
193
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 g_clear_pointer (&url, g_free);
194 10 url = msg_service_get_next_link (root_object);
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 } while (url != NULL);
196
197 10 return g_steal_pointer (&list);
198 }
199
200 /**
201 * msg_drive_service_get_root:
202 * @self: a #MsgDriveService
203 * @drive: a #MsgDrive
204 * @cancellable: a #GCancellable
205 * @error: a #GError
206 *
207 * Get root item of selected drive
208 *
209 * Returns: (transfer full): root 'MsgDriveItem'
210 */
211 MsgDriveItem *
212 9 msg_drive_service_get_root (MsgDriveService *self,
213 MsgDrive *drive,
214 GCancellable *cancellable,
215 GError **error)
216 {
217 9 g_autofree char *url = NULL;
218 9 g_autoptr (SoupMessage) message = NULL;
219 9 JsonObject *root_object = NULL;
220 9 g_autoptr (GBytes) response = NULL;
221 9 g_autoptr (JsonParser) parser = NULL;
222
223
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
224 return NULL;
225
226 9 url = g_strconcat (MSG_API_ENDPOINT, "/drives/", msg_drive_get_id (drive), "/root?select=id,remoteItem,file,folder,parentReference,name,createdBy,lastModifiedBy,createdDateTime,lastModifiedDateTime,size", NULL);
227 9 message = msg_service_build_message (MSG_SERVICE (self), "GET", url, NULL, FALSE);
228 9 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!parser)
230 return NULL;
231
232 9 return msg_drive_item_new_from_json (root_object, error);
233 }
234
235 /**
236 * msg_drive_service_download_item:
237 * @self: a #MsgDriveService
238 * @item: a #MsgDriveItem
239 * @cancellable: a #GCancellable
240 * @error: a #GError
241 *
242 * Download item
243 *
244 * Returns: (transfer full): input stream of drive item
245 */
246 GInputStream *
247 2 msg_drive_service_download_item (MsgDriveService *self,
248 MsgDriveItem *item,
249 GCancellable *cancellable,
250 GError **error)
251 {
252 2 g_autofree char *url = NULL;
253 2 const char *drive_id = NULL;
254 2 const char *id = NULL;
255
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!MSG_IS_DRIVE_ITEM_FILE (item)) {
257 g_warning ("Download only allowed for files");
258 return NULL;
259 }
260
261
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
262 return NULL;
263
264
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (!msg_drive_item_is_shared (item)) {
265 2 drive_id = msg_drive_item_get_drive_id (item);
266 2 id = msg_drive_item_get_id (item);
267 } else {
268 drive_id = msg_drive_item_get_remote_drive_id (item);
269 id = msg_drive_item_get_remote_id (item);
270 }
271
272 2 url = g_strconcat (MSG_API_ENDPOINT,
273 "/drives/",
274 drive_id,
275 "/items/",
276 id,
277 "/content",
278 NULL);
279
280 2 return msg_input_stream_new (MSG_SERVICE (self), url);
281 }
282
283 /**
284 * msg_drive_service_list_children:
285 * @self: a #MsgDriveService
286 * @item: a #MsgDriveItem
287 * @cancellable: a #GCancellable
288 * @error: a #GError
289 *
290 * Get a list of all files in folder item
291 *
292 * Returns: (element-type MsgDriveItem) (transfer full): all items in folder
293 */
294 GList *
295 1 msg_drive_service_list_children (MsgDriveService *self,
296 MsgDriveItem *item,
297 GCancellable *cancellable,
298 GError **error)
299 {
300 1 g_autoptr (MsgDriveItem) child_item = NULL;
301 1 g_autofree char *url = NULL;
302 1 JsonObject *root_object = NULL;
303 1 JsonArray *array = NULL;
304 1 guint array_length = 0, index = 0;
305 1 JsonObject *item_object = NULL;
306 1 g_autolist (MsgDriveItem) children = NULL;
307 1 g_autoptr (GBytes) response = NULL;
308 1 g_autoptr (JsonParser) parser = NULL;
309 1 gboolean add_prefer_header = self->type == MSG_DRIVE_TYPE_BUSINESS;
310 1 const char *drive_id = NULL;
311 1 const char *id = NULL;
312
313
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
314 return NULL;
315
316
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (!msg_drive_item_is_shared (item)) {
317 1 drive_id = msg_drive_item_get_drive_id (item);
318 1 id = msg_drive_item_get_id (item);
319 } else {
320 drive_id = msg_drive_item_get_remote_drive_id (item);
321 id = msg_drive_item_get_remote_id (item);
322 }
323
324 1 url = g_strconcat (MSG_API_ENDPOINT,
325 "/drives/",
326 drive_id,
327 "/items/",
328 id,
329 "/children",
330 "?$expand=thumbnails",
331 "&select=id,remoteItem,file,folder,parentReference,name,createdBy,lastModifiedBy,createdDateTime,lastModifiedDateTime,size",
332 NULL);
333
334 do {
335
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 g_autoptr (SoupMessage) message = NULL;
336
337 1 message = msg_service_build_message (MSG_SERVICE (self), "GET", url, NULL /*msg_drive_item_get_etag (item)*/, FALSE);
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (add_prefer_header)
339 soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", "Include-Feature=AddToOneDrive");
340
341 1 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!parser)
343 return NULL;
344
345 1 array = json_object_get_array_member (root_object, "value");
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (array != NULL);
347
348 1 array_length = json_array_get_length (array);
349
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (index = 0; index < array_length; index++) {
350
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 g_autoptr (GError) local_error = NULL;
351 5 item_object = json_array_get_object_element (array, index);
352
353 5 child_item = msg_drive_item_new_from_json (item_object, &local_error);
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (local_error) {
355 g_warning ("Could not read child item: %s", local_error->message);
356 continue;
357 }
358
359 5 children = g_list_prepend (children, g_steal_pointer (&child_item));
360 }
361
362
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&url, g_free);
363 1 url = msg_service_get_next_link (root_object);
364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } while (url != NULL);
365
366 1 return g_steal_pointer (&children);
367 }
368
369 /**
370 * msg_drive_service_rename:
371 * @self: a #MsgDriveService
372 * @item: a #MsgDriveItem
373 * @new_name: new name of item
374 * @cancellable: a #GCancellable
375 * @error: a #GError
376 *
377 * Sets a new drive item name
378 *
379 * Returns: (transfer full): the renamed `MsgDriveItem`
380 */
381 MsgDriveItem *
382 1 msg_drive_service_rename (MsgDriveService *self,
383 MsgDriveItem *item,
384 const char *new_name,
385 GCancellable *cancellable,
386 GError **error)
387 {
388 1 g_autoptr (SoupMessage) message = NULL;
389 1 g_autoptr (JsonBuilder) builder = NULL;
390 1 g_autoptr (JsonNode) rename_node = NULL;
391 1 g_autofree char *url = NULL;
392 1 g_autofree char *json = NULL;
393 1 JsonObject *root_object = NULL;
394 1 g_autoptr (GBytes) response = NULL;
395 1 g_autoptr (GBytes) body = NULL;
396 1 g_autoptr (JsonParser) parser = NULL;
397
398
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!is_valid_name (new_name)) {
399 g_set_error (error,
400 msg_error_quark (),
401 MSG_ERROR_FAILED,
402 "Invalid characters in name");
403 return NULL;
404 }
405
406
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
407 return NULL;
408
409 1 url = g_strconcat (MSG_API_ENDPOINT,
410 "/drives/",
411 msg_drive_item_get_drive_id (item),
412 "/items/",
413 msg_drive_item_get_id (item),
414 NULL);
415
416 1 message = msg_service_build_message (MSG_SERVICE (self), "PATCH", url, NULL, FALSE);
417
418 1 builder = json_builder_new ();
419 1 json_builder_begin_object (builder);
420 1 json_builder_set_member_name (builder, "name");
421 1 json_builder_add_string_value (builder, new_name);
422 1 json_builder_end_object (builder);
423 1 rename_node = json_builder_get_root (builder);
424 1 json = json_to_string (rename_node, TRUE);
425
426 1 body = g_bytes_new (json, strlen (json));
427 1 soup_message_set_request_body_from_bytes (message, "application/json", body);
428
429 1 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
430
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!parser)
431 return NULL;
432
433 1 return msg_drive_item_new_from_json (root_object, error);
434 }
435
436 /**
437 * msg_drive_service_download_url:
438 * @self: a #MsgDriveService
439 * @url: url to download
440 * @cancellable: a #GCancellable
441 * @error: a #GError
442 *
443 * Download url
444 *
445 * Returns: (transfer full): a new `GInputStream` for url
446 */
447 GInputStream *
448 1 msg_drive_service_download_url (MsgDriveService *self,
449 const char *url,
450 GCancellable *cancellable,
451 GError **error)
452 {
453 2 g_autoptr (SoupMessage) message = msg_service_build_message (MSG_SERVICE (self), "GET", url, NULL, FALSE);
454
455 1 return msg_service_send (MSG_SERVICE (self), message, cancellable, error);
456 }
457
458 /**
459 * msg_drive_service_create_folder:
460 * @self: a drive service
461 * @parent: parent drive item
462 * @name: name of new folder
463 * @cancellable: a cancellable
464 * @error: a error
465 *
466 * Creates a new folder called name under parent.
467 *
468 * Returns: (transfer full): a newly created `MsgDriveItem`
469 */
470 MsgDriveItem *
471 8 msg_drive_service_create_folder (MsgDriveService *self,
472 MsgDriveItem *parent,
473 const char *name,
474 GCancellable *cancellable,
475 GError **error)
476 {
477 8 g_autofree char *url = NULL;
478 8 g_autoptr (SoupMessage) message = NULL;
479 8 g_autofree char *json = NULL;
480 8 g_autoptr (JsonBuilder) builder = NULL;
481 8 g_autoptr (JsonNode) create_node = NULL;
482 8 JsonObject *root_object = NULL;
483 8 g_autoptr (GBytes) response = NULL;
484 8 g_autoptr (GBytes) body = NULL;
485 8 g_autoptr (JsonParser) parser = NULL;
486
487
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
488 return NULL;
489
490
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!is_valid_name (name)) {
491 g_set_error (error,
492 msg_error_quark (),
493 MSG_ERROR_FAILED,
494 "Invalid characters in name");
495 return NULL;
496 }
497
498 8 url = g_strconcat (MSG_API_ENDPOINT,
499 "/drives/",
500 msg_drive_item_get_drive_id (parent),
501 "/items/",
502 msg_drive_item_get_id (parent),
503 "/children",
504 NULL);
505
506 8 message = msg_service_build_message (MSG_SERVICE (self), "POST", url, NULL, FALSE);
507
508 8 builder = json_builder_new ();
509 8 json_builder_begin_object (builder);
510 8 json_builder_set_member_name (builder, "name");
511 8 json_builder_add_string_value (builder, name);
512 8 json_builder_set_member_name (builder, "@microsoft.graph.conflictBehavior");
513 8 json_builder_add_string_value (builder, "rename");
514 8 json_builder_set_member_name (builder, "folder");
515 8 json_builder_begin_object (builder);
516 8 json_builder_end_object (builder);
517 8 json_builder_end_object (builder);
518 8 create_node = json_builder_get_root (builder);
519 8 json = json_to_string (create_node, TRUE);
520
521 8 body = g_bytes_new (json, strlen (json));
522 8 soup_message_set_request_body_from_bytes (message, "application/json", body);
523
524 8 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
525
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!parser)
526 return NULL;
527
528 8 return msg_drive_item_new_from_json (root_object, error);
529 }
530
531 /**
532 * msg_drive_service_delete:
533 * @self: a drive service
534 * @item: a #MsgDriveItem
535 * @cancellable: a cancellable
536 * @error: a error
537 *
538 * Deletes item.
539 *
540 * Returns: %TRUE when item has been deleted, otherwise %FALSE
541 */
542 gboolean
543 11 msg_drive_service_delete (MsgDriveService *self,
544 MsgDriveItem *item,
545 GCancellable *cancellable,
546 GError **error)
547 {
548 11 g_autoptr (SoupMessage) message = NULL;
549 11 g_autofree char *url = NULL;
550 11 g_autoptr (GBytes) bytes = NULL;
551
552
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
553 return FALSE;
554
555 11 url = g_strconcat (MSG_API_ENDPOINT,
556 "/drives/",
557 msg_drive_item_get_drive_id (item),
558 "/items/",
559 msg_drive_item_get_id (item),
560 NULL);
561
562 11 message = msg_service_build_message (MSG_SERVICE (self), "DELETE", url, NULL, FALSE);
563
564 11 bytes = msg_service_send_and_read (MSG_SERVICE (self), message, cancellable, error);
565 11 return bytes != NULL;
566 }
567
568 /**
569 * msg_drive_service_update:
570 * @self: a drive service
571 * @item: a drive item
572 * @cancellable: a cancellable
573 * @error: a error
574 *
575 * Creates an update stream for drive item in order to update it's content.
576 *
577 * Returns: (transfer full): an output stream
578 */
579 GOutputStream *
580 7 msg_drive_service_update (MsgDriveService *self,
581 MsgDriveItem *item,
582 GCancellable *cancellable,
583 GError **error)
584 {
585 7 GOutputStream *stream = NULL;
586 7 g_autofree char *url = NULL;
587 7 g_autoptr (JsonBuilder) builder = NULL;
588 7 g_autoptr (JsonNode) create_node = NULL;
589 7 g_autoptr (SoupMessage) message = NULL;
590 7 JsonObject *root_object = NULL;
591 7 g_autofree char *json = NULL;
592 7 g_autoptr (GBytes) response = NULL;
593 7 g_autoptr (GBytes) body = NULL;
594 const char *upload_url;
595 7 g_autoptr (JsonParser) parser = NULL;
596
597
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
598 return NULL;
599
600 7 url = g_strconcat (MSG_API_ENDPOINT,
601 "/drives/",
602 msg_drive_item_get_drive_id (item),
603 "/items/",
604 msg_drive_item_get_id (item),
605 "/createUploadSession",
606 NULL);
607
608 7 message = msg_service_build_message (MSG_SERVICE (self), "POST", url, NULL, FALSE);
609 7 builder = json_builder_new ();
610 7 json_builder_begin_object (builder);
611 7 json_builder_set_member_name (builder, "@microsoft.graph.conflictBehavior");
612 7 json_builder_add_string_value (builder, "rename");
613 7 json_builder_end_object (builder);
614 7 create_node = json_builder_get_root (builder);
615 7 json = json_to_string (create_node, TRUE);
616
617 7 body = g_bytes_new (json, strlen (json));
618 7 soup_message_set_request_body_from_bytes (message, "application/json", body);
619
620 7 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!parser)
622 return NULL;
623
624
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (!json_object_has_member (root_object, "uploadUrl")) {
625 g_set_error (error,
626 msg_error_quark (),
627 MSG_ERROR_FAILED,
628 "No uploadUrl found");
629 return NULL;
630 }
631
632 7 upload_url = json_object_get_string_member (root_object, "uploadUrl");
633
634 7 stream = g_memory_output_stream_new_resizable ();
635 7 g_object_set_data_full (G_OBJECT (stream), "ms-graph-upload-url", g_strdup (upload_url), g_free);
636 7 g_object_set_data (G_OBJECT (stream), "ms-graph-item", item);
637
638 7 return stream;
639 }
640
641 /**
642 * msg_drive_service_update_finish:
643 * @self: a #MsgDriveService
644 * @item: a #MsgDriveItem
645 * @stream: stream where data is store and needs to be transfered
646 * @cancellable: a #GCancellable
647 * @error: a #GError
648 *
649 * Finish a update session of given #item.
650 *
651 * Returns: (transfer full): a new #MsgDriveItem or %NULL on error.
652 */
653 MsgDriveItem *
654 7 msg_drive_service_update_finish (MsgDriveService *self,
655 MsgDriveItem *item,
656 GOutputStream *stream,
657 GCancellable *cancellable,
658 GError **error)
659 {
660 7 g_autoptr (GBytes) bytes;
661 7 g_autofree char *url = NULL;
662 7 g_autoptr (SoupMessage) message = NULL;
663 7 JsonObject *root_object = NULL;
664 7 g_autoptr (GBytes) response = NULL;
665 7 g_autoptr (JsonParser) parser = NULL;
666
667
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
668 return NULL;
669
670 7 url = g_strconcat (MSG_API_ENDPOINT,
671 "/drives/",
672 msg_drive_item_get_drive_id (item),
673 "/items/",
674 msg_drive_item_get_id (item),
675 "/content",
676 NULL);
677
678 7 g_output_stream_close (stream, NULL, NULL);
679 7 bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
680
681 7 message = msg_service_build_message (MSG_SERVICE (self), "PUT", url, NULL, FALSE);
682 7 soup_message_set_request_body_from_bytes (message, "application/octet-stream", bytes);
683
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 g_clear_object (&stream);
684
685 7 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
686
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!parser)
687 return NULL;
688
689 7 return msg_drive_item_new_from_json (root_object, error);
690 }
691
692 /**
693 * msg_drive_service_add_item_to_folder:
694 * @self: a msg drive service
695 * @parent: parent drive item
696 * @item: drive item to add
697 * @cancellable: a cancellable
698 * @error: a error
699 *
700 * Adds item to parent folder
701 *
702 * Returns: (transfer full): a new drive item
703 */
704 MsgDriveItem *
705 7 msg_drive_service_add_item_to_folder (MsgDriveService *self,
706 MsgDriveItem *parent,
707 MsgDriveItem *item,
708 GCancellable *cancellable,
709 GError **error)
710 {
711 7 g_autofree char *url = NULL;
712 7 g_autoptr (SoupMessage) message = NULL;
713 7 g_autofree char *escaped_name = NULL;
714 7 JsonObject *root_object = NULL;
715 7 g_autoptr (GBytes) response = NULL;
716 7 g_autoptr (GBytes) body = NULL;
717 7 g_autoptr (JsonParser) parser = NULL;
718
719
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (!is_valid_name (msg_drive_item_get_name (item))) {
720 g_set_error (error,
721 msg_error_quark (),
722 MSG_ERROR_FAILED,
723 "Invalid characters in name");
724 return NULL;
725 }
726
727
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
728 return FALSE;
729
730 7 escaped_name = g_uri_escape_string (msg_drive_item_get_name (item), NULL, TRUE);
731 7 url = g_strconcat (MSG_API_ENDPOINT,
732 "/drives/",
733 msg_drive_item_get_drive_id (parent),
734 "/items/",
735 msg_drive_item_get_id (parent),
736 ":/",
737 escaped_name,
738 ":/content",
739 NULL);
740
741 7 message = msg_service_build_message (MSG_SERVICE (self), "PUT", url, NULL, FALSE);
742 7 body = g_bytes_new ("", 0);
743 7 soup_message_set_request_body_from_bytes (message, "text/plain", body);
744
745 7 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!parser)
747 return FALSE;
748
749 7 return msg_drive_item_new_from_json (root_object, error);
750 }
751
752 /**
753 * msg_drive_service_get_shared_with_me:
754 * @self: a #MsgDriveService
755 * @cancellable: a #GCancellable
756 * @error: a #GError
757 *
758 * Requests all shared with me items
759 *
760 * Returns: (element-type MsgDriveItem) (transfer full): shared with me list
761 */
762 GList *
763 1 msg_drive_service_get_shared_with_me (MsgDriveService *self,
764 GCancellable *cancellable,
765 GError **error)
766 {
767 1 g_autoptr (MsgDriveItem) child_item = NULL;
768 1 g_autofree char *url = NULL;
769 1 JsonObject *root_object = NULL;
770 1 JsonArray *array = NULL;
771 1 guint array_length = 0, index = 0;
772 1 JsonObject *item_object = NULL;
773 1 g_autolist (MsgDriveItem) children = NULL;
774 1 g_autoptr (GBytes) response = NULL;
775 1 g_autoptr (JsonParser) parser = NULL;
776
777
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, error))
778 return NULL;
779
780 1 url = g_strconcat (MSG_API_ENDPOINT,
781 "/me/drive/sharedWithMe",
782 "?select=id,remoteItem,file,folder,parentReference,name,createdBy,lastModifiedBy,createdDateTime,lastModifiedDateTime,size",
783 NULL);
784 do {
785
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 g_autoptr (SoupMessage) message = NULL;
786
787 1 message = msg_service_build_message (MSG_SERVICE (self), "GET", url, NULL, FALSE);
788 1 parser = msg_service_send_and_parse_response (MSG_SERVICE (self), message, &root_object, cancellable, error);
789
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!parser)
790 return NULL;
791
792 1 array = json_object_get_array_member (root_object, "value");
793
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (array != NULL);
794
795 1 array_length = json_array_get_length (array);
796
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (index = 0; index < array_length; index++) {
797
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 g_autoptr (GError) local_error = NULL;
798 4 item_object = json_array_get_object_element (array, index);
799
800 4 child_item = msg_drive_item_new_from_json (item_object, &local_error);
801
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (local_error) {
802 g_warning ("Could not load shared with me item: %s", local_error->message);
803 continue;
804 }
805
806 4 children = g_list_prepend (children, g_steal_pointer (&child_item));
807 }
808
809
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&url, g_free);
810 1 url = msg_service_get_next_link (root_object);
811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } while (url != NULL);
812
813 1 return g_steal_pointer (&children);
814 }
815
816 /**
817 * msg_drive_service_copy_file:
818 * @self: #MsgDriveService
819 * @file: source #MsgDriveItem
820 * @destination: destination directory #MsgDriveItem
821 * @cancellable: a #GCancellable
822 * @error: a #GError
823 *
824 * Copy a file async on remote server to a new directory.
825 *
826 * Returns: %TRUE if accepted, %FALSE on error
827 */
828 gboolean
829 1 msg_drive_service_copy_file (MsgDriveService *self,
830 MsgDriveItem *file,
831 MsgDriveItem *destination,
832 GCancellable *cancellable,
833 GError **error)
834 {
835 1 g_autofree char *url = NULL;
836 1 g_autoptr (SoupMessage) message = NULL;
837 1 g_autofree char *json = NULL;
838 1 g_autoptr (JsonBuilder) builder = NULL;
839 1 g_autoptr (JsonNode) create_node = NULL;
840 1 GError *local_error = NULL;
841 1 g_autoptr (GBytes) response = NULL;
842 1 g_autoptr (GBytes) body = NULL;
843 1 g_autoptr (JsonParser) parser = NULL;
844
845
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, &local_error)) {
846 g_propagate_error (error, g_steal_pointer (&local_error));
847 return FALSE;
848 }
849
850 1 url = g_strconcat (MSG_API_ENDPOINT,
851 "/drives/",
852 msg_drive_item_get_drive_id (file),
853 "/items/",
854 msg_drive_item_get_id (file),
855 "/copy",
856 NULL);
857
858 1 message = msg_service_build_message (MSG_SERVICE (self), "POST", url, NULL, FALSE);
859
860 1 builder = json_builder_new ();
861 1 json_builder_begin_object (builder);
862
863 1 json_builder_set_member_name (builder, "parentReference");
864 1 json_builder_begin_object (builder);
865 1 json_builder_set_member_name (builder, "driveId");
866 1 json_builder_add_string_value (builder, msg_drive_item_get_drive_id (destination));
867 1 json_builder_set_member_name (builder, "id");
868 1 json_builder_add_string_value (builder, msg_drive_item_get_id (destination));
869 1 json_builder_end_object (builder);
870
871 1 json_builder_end_object (builder);
872 1 create_node = json_builder_get_root (builder);
873 1 json = json_to_string (create_node, TRUE);
874
875 1 body = g_bytes_new (json, strlen (json));
876 1 soup_message_set_request_body_from_bytes (message, "application/json", body);
877
878 1 response = msg_service_send_and_read (MSG_SERVICE (self), message, cancellable, &local_error);
879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (local_error) {
880 g_propagate_error (error, g_steal_pointer (&local_error));
881 return FALSE;
882 }
883
884 1 return TRUE;
885 }
886
887 /**
888 * msg_drive_service_move_file:
889 * @self: #MsgDriveService
890 * @file: source #MsgDriveItem
891 * @destination: destination directory #MsgDriveItem
892 * @cancellable: a #GCancellable
893 * @error: a #GError
894 *
895 * Move a file async on remote server to a new directory.
896 *
897 * Returns: (transfer full): moved #MsgDriveItem
898 */
899 MsgDriveItem *
900 msg_drive_service_move_file (MsgDriveService *self,
901 MsgDriveItem *file,
902 MsgDriveItem *destination,
903 GCancellable *cancellable,
904 GError **error)
905 {
906 g_autofree char *url = NULL;
907 g_autoptr (SoupMessage) message = NULL;
908 g_autofree char *json = NULL;
909 g_autoptr (JsonBuilder) builder = NULL;
910 g_autoptr (JsonNode) create_node = NULL;
911 GError *local_error = NULL;
912 g_autoptr (GBytes) response = NULL;
913 g_autoptr (GBytes) body = NULL;
914 g_autoptr (JsonParser) parser = NULL;
915 MsgDriveItem *item = NULL;
916 JsonObject *root_object = NULL;
917
918 if (!msg_service_refresh_authorization (MSG_SERVICE (self), cancellable, &local_error)) {
919 g_propagate_error (error, g_steal_pointer (&local_error));
920 return NULL;
921 }
922
923 url = g_strconcat (MSG_API_ENDPOINT,
924 "/drives/",
925 msg_drive_item_get_drive_id (file),
926 "/items/",
927 msg_drive_item_get_id (file),
928 NULL);
929
930 message = msg_service_build_message (MSG_SERVICE (self), "PATCH", url, NULL, FALSE);
931
932 builder = json_builder_new ();
933 json_builder_begin_object (builder);
934
935 json_builder_set_member_name (builder, "parentReference");
936 json_builder_begin_object (builder);
937 json_builder_set_member_name (builder, "id");
938 json_builder_add_string_value (builder, msg_drive_item_get_id (destination));
939 json_builder_end_object (builder);
940
941 json_builder_end_object (builder);
942 create_node = json_builder_get_root (builder);
943 json = json_to_string (create_node, TRUE);
944
945 body = g_bytes_new (json, strlen (json));
946 soup_message_set_request_body_from_bytes (message, "application/json", body);
947
948 response = msg_service_send_and_read (MSG_SERVICE (self), message, cancellable, &local_error);
949 if (local_error) {
950 g_propagate_error (error, g_steal_pointer (&local_error));
951 return NULL;
952 }
953
954 parser = msg_service_parse_response (response, &root_object, &local_error);
955 if (local_error != NULL) {
956 g_propagate_error (error, g_steal_pointer (&local_error));
957 return NULL;
958 }
959
960 item = msg_drive_item_new_from_json (root_object, &local_error);
961 if (local_error) {
962 g_propagate_error (error, g_steal_pointer (&local_error));
963 return FALSE;
964 }
965
966 return item;
967 }
968