Contents of /trunk/src/osm_api.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 175 - (show annotations)
Wed Jun 10 09:24:47 2009 UTC (15 years ago) by harbaum
File MIME type: text/plain
File size: 34097 byte(s)
Adjusted default path
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21
22 #include <curl/curl.h>
23 #include <curl/types.h> /* new for v7 */
24 #include <curl/easy.h> /* new for v7 */
25 #include <unistd.h>
26
27 #define COLOR_ERR "red"
28 #define COLOR_OK "darkgreen"
29
30 #define NO_EXPECT
31
32 static struct http_message_s {
33 int id;
34 char *msg;
35 } http_messages [] = {
36 { 200, "Ok" },
37 { 203, "No Content" },
38 { 301, "Moved Permenently" },
39 { 302, "Moved Temporarily" },
40 { 400, "Bad Request" },
41 { 401, "Unauthorized" },
42 { 403, "Forbidden" },
43 { 404, "Not Found" },
44 { 405, "Method Not Allowed" },
45 { 409, "Conflict" },
46 { 410, "Gone" },
47 { 412, "Precondition Failed" },
48 { 417, "(Expect rejected)" },
49 { 500, "Internal Server Error" },
50 { 503, "Service Unavailable" },
51 { 0, NULL }
52 };
53
54 static char *osm_http_message(int id) {
55 struct http_message_s *msg = http_messages;
56
57 while(msg->id) {
58 if(msg->id == id) return _(msg->msg);
59 msg++;
60 }
61
62 return NULL;
63 }
64
65 typedef struct {
66 appdata_t *appdata;
67 GtkWidget *dialog;
68 osm_t *osm;
69 project_t *project;
70
71 struct log_s {
72 GtkTextBuffer *buffer;
73 GtkWidget *view;
74 } log;
75
76 item_id_t changeset;
77 char *comment;
78
79 proxy_t *proxy;
80 } osm_upload_context_t;
81
82 gboolean osm_download(GtkWidget *parent, settings_t *settings,
83 project_t *project) {
84 printf("download osm ...\n");
85
86 g_assert(project->server);
87
88 /* check if server name contains string "0.5" and adjust it */
89 if(strstr(project->server, "0.5") != NULL) {
90 strstr(project->server, "0.5")[2] = '6';
91
92 messagef(parent, _("Server changed"),
93 _("It seems your current project uses a server/protocol no "
94 "longer in use by OSM. It has thus been changed to:\n\n%s"),
95 project->server);
96 project->dirty = TRUE; // project needs to be changed
97 } else
98 printf("url ok\n");
99
100 char minlon[G_ASCII_DTOSTR_BUF_SIZE], minlat[G_ASCII_DTOSTR_BUF_SIZE];
101 char maxlon[G_ASCII_DTOSTR_BUF_SIZE], maxlat[G_ASCII_DTOSTR_BUF_SIZE];
102
103 g_ascii_formatd(minlon, sizeof(minlon), LL_FORMAT, project->min.lon);
104 g_ascii_formatd(minlat, sizeof(minlat), LL_FORMAT, project->min.lat);
105 g_ascii_formatd(maxlon, sizeof(maxlon), LL_FORMAT, project->max.lon);
106 g_ascii_formatd(maxlat, sizeof(maxlat), LL_FORMAT, project->max.lat);
107
108 /* server url should not end with a slash */
109 if(project->server[strlen(project->server)-1] == '/') {
110 printf("removing trailing slash\n");
111 project->server[strlen(project->server)-1] = 0;
112 project->dirty = TRUE;
113 }
114
115 char *url = g_strdup_printf("%s/map?bbox=%s,%s,%s,%s",
116 project->server, minlon, minlat, maxlon, maxlat);
117
118 char *str = NULL;
119 if(project->osm[0] == '/') str = g_strdup(project->osm);
120 else str = g_strjoin(NULL, project->path, project->osm, NULL);
121 gboolean result = net_io_download_file(parent, settings, url, str);
122 g_free(str);
123
124 g_free(url);
125 return result;
126 }
127
128 typedef struct {
129 char *ptr;
130 int len;
131 } curl_data_t;
132
133 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
134 curl_data_t *p = (curl_data_t*)stream;
135
136 // printf("request to read %d items of size %d, pointer = %p\n",
137 // nmemb, size, p->ptr);
138
139 if(nmemb*size > p->len)
140 nmemb = p->len/size;
141
142 memcpy(ptr, p->ptr, size*nmemb);
143 p->ptr += size*nmemb;
144 p->len -= size*nmemb;
145
146 return nmemb;
147 }
148
149 static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
150 curl_data_t *p = (curl_data_t*)stream;
151
152 p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1);
153 if(p->ptr) {
154 memcpy(p->ptr+p->len, ptr, size*nmemb);
155 p->len += size*nmemb;
156 p->ptr[p->len] = 0;
157 }
158 return nmemb;
159 }
160
161 static void appendf(struct log_s *log, char *colname,
162 const char *fmt, ...) {
163 va_list args;
164 va_start( args, fmt );
165 char *buf = g_strdup_vprintf(fmt, args);
166 va_end( args );
167
168 printf(buf);
169
170 GtkTextTag *tag = NULL;
171 if(colname)
172 tag = gtk_text_buffer_create_tag(log->buffer, NULL,
173 "foreground", colname,
174 NULL);
175
176 GtkTextIter end;
177 gtk_text_buffer_get_end_iter(log->buffer, &end);
178 if(tag)
179 gtk_text_buffer_insert_with_tags(log->buffer, &end, buf, -1, tag, NULL);
180 else
181 gtk_text_buffer_insert(log->buffer, &end, buf, -1);
182
183 g_free(buf);
184
185 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
186 &end, 0.0, FALSE, 0, 0);
187
188 while(gtk_events_pending())
189 gtk_main_iteration();
190 }
191
192 #define MAX_TRY 5
193
194 static gboolean osm_update_item(struct log_s *log, char *xml_str,
195 char *url, char *user, item_id_t *id,
196 proxy_t *proxy) {
197 int retry = MAX_TRY;
198 char buffer[CURL_ERROR_SIZE];
199
200 CURL *curl;
201 CURLcode res;
202
203 curl_data_t read_data;
204 curl_data_t write_data;
205
206 while(retry >= 0) {
207
208 if(retry != MAX_TRY)
209 appendf(log, NULL, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1);
210
211 /* get a curl handle */
212 curl = curl_easy_init();
213 if(!curl) {
214 appendf(log, NULL, _("CURL init error\n"));
215 return FALSE;
216 }
217
218 read_data.ptr = xml_str;
219 read_data.len = xml_str?strlen(xml_str):0;
220 write_data.ptr = NULL;
221 write_data.len = 0;
222
223 /* we want to use our own read/write functions */
224 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
225 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
226
227 /* enable uploading */
228 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
229
230 /* specify target URL, and note that this URL should include a file
231 name, not only a directory */
232 curl_easy_setopt(curl, CURLOPT_URL, url);
233
234 /* now specify which file to upload */
235 curl_easy_setopt(curl, CURLOPT_READDATA, &read_data);
236
237 /* provide the size of the upload, we specicially typecast the value
238 to curl_off_t since we must be sure to use the correct data size */
239 curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)read_data.len);
240
241 /* we pass our 'chunk' struct to the callback function */
242 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);
243
244 /* some servers don't like requests that are made without a user-agent
245 field, so we provide one */
246 curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION);
247
248 #ifdef NO_EXPECT
249 struct curl_slist *slist = NULL;
250 slist = curl_slist_append(slist, "Expect:");
251 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
252 #endif
253
254 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
255
256 /* set user name and password for the authentication */
257 curl_easy_setopt(curl, CURLOPT_USERPWD, user);
258
259 net_io_set_proxy(curl, proxy);
260
261 /* Now run off and do what you've been told! */
262 res = curl_easy_perform(curl);
263
264 long response;
265 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
266
267 /* always cleanup */
268 #ifdef NO_EXPECT
269 curl_slist_free_all(slist);
270 #endif
271 curl_easy_cleanup(curl);
272
273 /* this will return the id on a successful create */
274 if(id && (res == 0) && (response == 200)) {
275 printf("request to parse successful reply as an id\n");
276 *id = strtoul(write_data.ptr, NULL, 10);
277 }
278
279 if(res != 0)
280 appendf(log, COLOR_ERR, _("failed: %s\n"), buffer);
281 else if(response != 200)
282 appendf(log, COLOR_ERR, _("failed, code: %ld %s\n"),
283 response, osm_http_message(response));
284 else {
285 if(!id) appendf(log, COLOR_OK, _("ok\n"));
286 else appendf(log, COLOR_OK, _("ok: #%ld\n"), *id);
287 }
288
289 /* if it's neither "ok" (200), nor "internal server error" (500) */
290 /* then write the message to the log */
291 if((response != 200) && (response != 500) && write_data.ptr) {
292 appendf(log, NULL, _("Server reply: "));
293 appendf(log, COLOR_ERR, _("%s\n"), write_data.ptr);
294 }
295
296 if(write_data.ptr)
297 g_free(write_data.ptr);
298
299 /* don't retry unless we had an "internal server error" */
300 if(response != 500)
301 return((res == 0)&&(response == 200));
302
303 retry--;
304 }
305
306 return FALSE;
307 }
308
309 static gboolean osm_delete_item(struct log_s *log, char *xml_str,
310 char *url, char *user, proxy_t *proxy) {
311 int retry = MAX_TRY;
312 char buffer[CURL_ERROR_SIZE];
313
314 CURL *curl;
315 CURLcode res;
316
317 /* delete has a payload since api 0.6 */
318 curl_data_t read_data;
319 curl_data_t write_data;
320
321 while(retry >= 0) {
322
323 if(retry != MAX_TRY)
324 appendf(log, NULL, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1);
325
326 /* get a curl handle */
327 curl = curl_easy_init();
328 if(!curl) {
329 appendf(log, NULL, _("CURL init error\n"));
330 return FALSE;
331 }
332
333 read_data.ptr = xml_str;
334 read_data.len = xml_str?strlen(xml_str):0;
335 write_data.ptr = NULL;
336 write_data.len = 0;
337
338 /* we want to use our own read/write functions */
339 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
340 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
341
342 curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)read_data.len);
343
344 /* now specify which file to upload */
345 curl_easy_setopt(curl, CURLOPT_READDATA, &read_data);
346
347 /* we pass our 'chunk' struct to the callback function */
348 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);
349
350 /* enable uploading */
351 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
352
353 /* no read/write functions required */
354 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
355
356 /* specify target URL, and note that this URL should include a file
357 name, not only a directory */
358 curl_easy_setopt(curl, CURLOPT_URL, url);
359
360 /* some servers don't like requests that are made without a user-agent
361 field, so we provide one */
362 curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION);
363
364 #ifdef NO_EXPECT
365 struct curl_slist *slist = NULL;
366 slist = curl_slist_append(slist, "Expect:");
367 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
368 #endif
369
370 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
371
372 /* set user name and password for the authentication */
373 curl_easy_setopt(curl, CURLOPT_USERPWD, user);
374
375 net_io_set_proxy(curl, proxy);
376
377 /* Now run off and do what you've been told! */
378 res = curl_easy_perform(curl);
379
380 long response;
381 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
382
383 /* always cleanup */
384 #ifdef NO_EXPECT
385 curl_slist_free_all(slist);
386 #endif
387 curl_easy_cleanup(curl);
388
389 if(res != 0)
390 appendf(log, COLOR_ERR, _("failed: %s\n"), buffer);
391 else if(response != 200)
392 appendf(log, COLOR_ERR, _("failed, code: %ld %s\n"),
393 response, osm_http_message(response));
394 else
395 appendf(log, COLOR_OK, _("ok\n"));
396
397 /* if it's neither "ok" (200), nor "internal server error" (500) */
398 /* then write the message to the log */
399 if((response != 200) && (response != 500) && write_data.ptr) {
400 appendf(log, NULL, _("Server reply: "));
401 appendf(log, COLOR_ERR, _("%s\n"), write_data.ptr);
402 }
403
404 if(write_data.ptr)
405 g_free(write_data.ptr);
406
407 /* don't retry unless we had an "internal server error" */
408 if(response != 500)
409 return((res == 0)&&(response == 200));
410
411 retry--;
412 }
413
414 return FALSE;
415 }
416
417 typedef struct {
418 struct {
419 int total, new, dirty, deleted;
420 } ways, nodes, relations;
421 } osm_dirty_t;
422
423 static GtkWidget *table_attach_label_c(GtkWidget *table, char *str,
424 int x1, int x2, int y1, int y2) {
425 GtkWidget *label = gtk_label_new(str);
426 gtk_table_attach_defaults(GTK_TABLE(table), label, x1, x2, y1, y2);
427 return label;
428 }
429
430 static GtkWidget *table_attach_label_l(GtkWidget *table, char *str,
431 int x1, int x2, int y1, int y2) {
432 GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2);
433 gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
434 return label;
435 }
436
437 static GtkWidget *table_attach_int(GtkWidget *table, int num,
438 int x1, int x2, int y1, int y2) {
439 char *str = g_strdup_printf("%d", num);
440 GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2);
441 g_free(str);
442 return label;
443 }
444
445 static void osm_delete_nodes(osm_upload_context_t *context) {
446 node_t *node = context->osm->node;
447 project_t *project = context->project;
448
449 while(node) {
450 /* make sure gui gets updated */
451 while(gtk_events_pending()) gtk_main_iteration();
452
453 if(node->flags & OSM_FLAG_DELETED) {
454 printf("deleting node on server\n");
455
456 appendf(&context->log, NULL, _("Delete node #%ld "), node->id);
457
458 char *url = g_strdup_printf("%s/node/" ITEM_ID_FORMAT,
459 project->server, node->id);
460 char *cred = g_strdup_printf("%s:%s",
461 context->appdata->settings->username,
462 context->appdata->settings->password);
463
464 char *xml_str =
465 osm_generate_xml_node(context->osm, context->changeset, node);
466
467 if(osm_delete_item(&context->log, xml_str, url, cred, context->proxy)) {
468 node->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
469 project->data_dirty = TRUE;
470 }
471
472 g_free(cred);
473 }
474 node = node->next;
475 }
476 }
477
478 static void osm_upload_nodes(osm_upload_context_t *context) {
479 node_t *node = context->osm->node;
480 project_t *project = context->project;
481
482 while(node) {
483 /* make sure gui gets updated */
484 while(gtk_events_pending()) gtk_main_iteration();
485
486 if((node->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
487 (!(node->flags & OSM_FLAG_DELETED))) {
488 char *url = NULL;
489
490 if(node->flags & OSM_FLAG_NEW) {
491 url = g_strdup_printf("%s/node/create", project->server);
492 appendf(&context->log, NULL, _("New node "));
493 } else {
494 url = g_strdup_printf("%s/node/" ITEM_ID_FORMAT,
495 project->server, node->id);
496 appendf(&context->log, NULL, _("Modified node #%ld "), node->id);
497 }
498
499 /* upload this node */
500 char *xml_str =
501 osm_generate_xml_node(context->osm, context->changeset, node);
502 if(xml_str) {
503 printf("uploading node %s from address %p\n", url, xml_str);
504
505 char *cred = g_strdup_printf("%s:%s",
506 context->appdata->settings->username,
507 context->appdata->settings->password);
508 if(osm_update_item(&context->log, xml_str, url, cred,
509 (node->flags & OSM_FLAG_NEW)?&(node->id):&(node->version),
510 context->proxy)) {
511 node->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
512 project->data_dirty = TRUE;
513 }
514 g_free(cred);
515 }
516 g_free(url);
517 }
518 node = node->next;
519 }
520 }
521
522 static void osm_delete_ways(osm_upload_context_t *context) {
523 way_t *way = context->osm->way;
524 project_t *project = context->project;
525
526 while(way) {
527 /* make sure gui gets updated */
528 while(gtk_events_pending()) gtk_main_iteration();
529
530 if(way->flags & OSM_FLAG_DELETED) {
531 printf("deleting way on server\n");
532
533 appendf(&context->log, NULL, _("Delete way #%ld "), way->id);
534
535 char *url = g_strdup_printf("%s/way/" ITEM_ID_FORMAT,
536 project->server, way->id);
537 char *cred = g_strdup_printf("%s:%s",
538 context->appdata->settings->username,
539 context->appdata->settings->password);
540
541 char *xml_str =
542 osm_generate_xml_way(context->osm, context->changeset, way);
543
544 if(osm_delete_item(&context->log, xml_str, url, cred, context->proxy)) {
545 way->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
546 project->data_dirty = TRUE;
547 }
548
549 g_free(cred);
550 }
551 way = way->next;
552 }
553 }
554
555
556 static void osm_upload_ways(osm_upload_context_t *context) {
557 way_t *way = context->osm->way;
558 project_t *project = context->project;
559
560 while(way) {
561 /* make sure gui gets updated */
562 while(gtk_events_pending()) gtk_main_iteration();
563
564 if((way->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
565 (!(way->flags & OSM_FLAG_DELETED))) {
566 char *url = NULL;
567
568 if(way->flags & OSM_FLAG_NEW) {
569 url = g_strdup_printf("%s/way/create", project->server);
570 appendf(&context->log, NULL, _("New way "));
571 } else {
572 url = g_strdup_printf("%s/way/" ITEM_ID_FORMAT,
573 project->server, way->id);
574 appendf(&context->log, NULL, _("Modified way #%ld "), way->id);
575 }
576
577 /* upload this node */
578 char *xml_str =
579 osm_generate_xml_way(context->osm, context->changeset, way);
580 if(xml_str) {
581 printf("uploading way %s from address %p\n", url, xml_str);
582
583 char *cred = g_strdup_printf("%s:%s",
584 context->appdata->settings->username,
585 context->appdata->settings->password);
586 if(osm_update_item(&context->log, xml_str, url, cred,
587 (way->flags & OSM_FLAG_NEW)?&(way->id):&(way->version),
588 context->proxy)) {
589 way->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
590 project->data_dirty = TRUE;
591 }
592 g_free(cred);
593 }
594 g_free(url);
595 }
596 way = way->next;
597 }
598 }
599
600 static void osm_delete_relations(osm_upload_context_t *context) {
601 relation_t *relation = context->osm->relation;
602 project_t *project = context->project;
603
604 while(relation) {
605 /* make sure gui gets updated */
606 while(gtk_events_pending()) gtk_main_iteration();
607
608 if(relation->flags & OSM_FLAG_DELETED) {
609 printf("deleting relation on server\n");
610
611 appendf(&context->log, NULL, _("Delete relation #%ld "), relation->id);
612
613 char *url = g_strdup_printf("%s/relation/" ITEM_ID_FORMAT,
614 project->server, relation->id);
615 char *cred = g_strdup_printf("%s:%s",
616 context->appdata->settings->username,
617 context->appdata->settings->password);
618
619 char *xml_str =
620 osm_generate_xml_relation(context->osm, context->changeset, relation);
621
622 if(osm_delete_item(&context->log, xml_str, url, cred, context->proxy)) {
623 relation->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
624 project->data_dirty = TRUE;
625 }
626
627 g_free(cred);
628 }
629 relation = relation->next;
630 }
631 }
632
633
634 static void osm_upload_relations(osm_upload_context_t *context) {
635 relation_t *relation = context->osm->relation;
636 project_t *project = context->project;
637
638 while(relation) {
639 /* make sure gui gets updated */
640 while(gtk_events_pending()) gtk_main_iteration();
641
642 if((relation->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
643 (!(relation->flags & OSM_FLAG_DELETED))) {
644 char *url = NULL;
645
646 if(relation->flags & OSM_FLAG_NEW) {
647 url = g_strdup_printf("%s/relation/create", project->server);
648 appendf(&context->log, NULL, _("New relation "));
649 } else {
650 url = g_strdup_printf("%s/relation/" ITEM_ID_FORMAT,
651 project->server,relation->id);
652 appendf(&context->log, NULL, _("Modified relation #%ld "),
653 relation->id);
654 }
655
656 /* upload this relation */
657 char *xml_str =
658 osm_generate_xml_relation(context->osm, context->changeset, relation);
659 if(xml_str) {
660 printf("uploading relation %s from address %p\n", url, xml_str);
661
662 char *cred = g_strdup_printf("%s:%s",
663 context->appdata->settings->username,
664 context->appdata->settings->password);
665 if(osm_update_item(&context->log, xml_str, url, cred,
666 (relation->flags & OSM_FLAG_NEW)?&(relation->id):&(relation->version),
667 context->proxy)) {
668 relation->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
669 project->data_dirty = TRUE;
670 }
671 g_free(cred);
672 }
673 g_free(url);
674 }
675 relation = relation->next;
676 }
677 }
678
679 static gboolean osm_create_changeset(osm_upload_context_t *context) {
680 gboolean result = FALSE;
681 context->changeset = ILLEGAL;
682 project_t *project = context->project;
683
684 /* make sure gui gets updated */
685 while(gtk_events_pending()) gtk_main_iteration();
686
687 char *url = g_strdup_printf("%s/changeset/create", project->server);
688 appendf(&context->log, NULL, _("Create changeset "));
689
690 /* create changeset request */
691 char *xml_str = osm_generate_xml_changeset(context->osm, context->comment);
692 if(xml_str) {
693 printf("creating changeset %s from address %p\n", url, xml_str);
694
695 char *cred = g_strdup_printf("%s:%s",
696 context->appdata->settings->username,
697 context->appdata->settings->password);
698
699 if(osm_update_item(&context->log, xml_str, url, cred,
700 &context->changeset, context->proxy)) {
701 printf("got changeset id " ITEM_ID_FORMAT "\n", context->changeset);
702 result = TRUE;
703 }
704
705 g_free(cred);
706 }
707 g_free(url);
708
709 return result;
710 }
711
712 static gboolean osm_close_changeset(osm_upload_context_t *context) {
713 gboolean result = FALSE;
714 project_t *project = context->project;
715
716 g_assert(context->changeset != ILLEGAL);
717
718 /* make sure gui gets updated */
719 while(gtk_events_pending()) gtk_main_iteration();
720
721 char *url = g_strdup_printf("%s/changeset/" ITEM_ID_FORMAT "/close",
722 project->server, context->changeset);
723 appendf(&context->log, NULL, _("Close changeset "));
724
725 char *cred = g_strdup_printf("%s:%s",
726 context->appdata->settings->username,
727 context->appdata->settings->password);
728
729 if(osm_update_item(&context->log, NULL, url, cred, NULL, context->proxy))
730 result = TRUE;
731
732 g_free(cred);
733 g_free(url);
734
735 return result;
736 }
737
738 /* comment buffer has been edited, allow upload if the buffer is not empty */
739 static void callback_buffer_modified(GtkTextBuffer *buffer, GtkDialog *dialog) {
740 GtkTextIter start, end;
741 gtk_text_buffer_get_start_iter(buffer, &start);
742 gtk_text_buffer_get_end_iter(buffer, &end);
743 char *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
744 gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT,
745 text && strlen(text));
746 }
747
748 static gboolean cb_focus_in(GtkTextView *view, GdkEventFocus *event,
749 GtkTextBuffer *buffer) {
750
751 gboolean first_click =
752 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(view), "first_click"));
753
754 g_object_set_data(G_OBJECT(view), "first_click", GINT_TO_POINTER(FALSE));
755
756 if(first_click) {
757 GtkTextIter start, end;
758 gtk_text_buffer_get_start_iter(buffer, &start);
759 gtk_text_buffer_get_end_iter(buffer, &end);
760 gtk_text_buffer_delete(buffer, &start, &end);
761 }
762
763 return FALSE;
764 }
765
766 void osm_upload(appdata_t *appdata, osm_t *osm, project_t *project) {
767
768 printf("starting upload\n");
769
770 /* upload config and confirmation dialog */
771
772 /* count nodes */
773 osm_dirty_t dirty;
774 memset(&dirty, 0, sizeof(osm_dirty_t));
775
776 node_t *node = osm->node;
777 while(node) {
778 dirty.nodes.total++;
779 if(node->flags & OSM_FLAG_DELETED) dirty.nodes.deleted++;
780 else if(node->flags & OSM_FLAG_NEW) dirty.nodes.new++;
781 else if(node->flags & OSM_FLAG_DIRTY) dirty.nodes.dirty++;
782
783 node = node->next;
784 }
785 printf("nodes: new %2d, dirty %2d, deleted %2d\n",
786 dirty.nodes.new, dirty.nodes.dirty, dirty.nodes.deleted);
787
788 /* count ways */
789 way_t *way = osm->way;
790 while(way) {
791 dirty.ways.total++;
792 if(way->flags & OSM_FLAG_DELETED) dirty.ways.deleted++;
793 else if(way->flags & OSM_FLAG_NEW) dirty.ways.new++;
794 else if(way->flags & OSM_FLAG_DIRTY) dirty.ways.dirty++;
795
796 way = way->next;
797 }
798 printf("ways: new %2d, dirty %2d, deleted %2d\n",
799 dirty.ways.new, dirty.ways.dirty, dirty.ways.deleted);
800
801 /* count relations */
802 relation_t *relation = osm->relation;
803 while(relation) {
804 dirty.relations.total++;
805 if(relation->flags & OSM_FLAG_DELETED) dirty.relations.deleted++;
806 else if(relation->flags & OSM_FLAG_NEW) dirty.relations.new++;
807 else if(relation->flags & OSM_FLAG_DIRTY) dirty.relations.dirty++;
808
809 relation = relation->next;
810 }
811 printf("relations: new %2d, dirty %2d, deleted %2d\n",
812 dirty.relations.new, dirty.relations.dirty, dirty.relations.deleted);
813
814
815 GtkWidget *dialog =
816 misc_dialog_new(MISC_DIALOG_NOSIZE, _("Upload to OSM"),
817 GTK_WINDOW(appdata->window),
818 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
819 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
820 NULL);
821
822 GtkWidget *table = gtk_table_new(4, 5, TRUE);
823
824 table_attach_label_c(table, _("Total"), 1, 2, 0, 1);
825 table_attach_label_c(table, _("New"), 2, 3, 0, 1);
826 table_attach_label_c(table, _("Modified"), 3, 4, 0, 1);
827 table_attach_label_c(table, _("Deleted"), 4, 5, 0, 1);
828
829 table_attach_label_l(table, _("Nodes:"), 0, 1, 1, 2);
830 table_attach_int(table, dirty.nodes.total, 1, 2, 1, 2);
831 table_attach_int(table, dirty.nodes.new, 2, 3, 1, 2);
832 table_attach_int(table, dirty.nodes.dirty, 3, 4, 1, 2);
833 table_attach_int(table, dirty.nodes.deleted, 4, 5, 1, 2);
834
835 table_attach_label_l(table, _("Ways:"), 0, 1, 2, 3);
836 table_attach_int(table, dirty.ways.total, 1, 2, 2, 3);
837 table_attach_int(table, dirty.ways.new, 2, 3, 2, 3);
838 table_attach_int(table, dirty.ways.dirty, 3, 4, 2, 3);
839 table_attach_int(table, dirty.ways.deleted, 4, 5, 2, 3);
840
841 table_attach_label_l(table, _("Relations:"), 0, 1, 3, 4);
842 table_attach_int(table, dirty.relations.total, 1, 2, 3, 4);
843 table_attach_int(table, dirty.relations.new, 2, 3, 3, 4);
844 table_attach_int(table, dirty.relations.dirty, 3, 4, 3, 4);
845 table_attach_int(table, dirty.relations.deleted, 4, 5, 3, 4);
846
847 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
848
849 /* ------------------------------------------------------ */
850
851 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
852 gtk_hseparator_new());
853
854 /* ------- add username and password entries ------------ */
855
856 table = gtk_table_new(2, 2, FALSE);
857 table_attach_label_l(table, _("Username:"), 0, 1, 0, 1);
858 GtkWidget *uentry = gtk_entry_new();
859 HILDON_ENTRY_NO_AUTOCAP(uentry);
860 gtk_entry_set_text(GTK_ENTRY(uentry), appdata->settings->username);
861 gtk_table_attach_defaults(GTK_TABLE(table), uentry, 1, 2, 0, 1);
862 table_attach_label_l(table, _("Password:"), 0, 1, 1, 2);
863 GtkWidget *pentry = gtk_entry_new();
864 HILDON_ENTRY_NO_AUTOCAP(pentry);
865 gtk_entry_set_text(GTK_ENTRY(pentry), appdata->settings->password);
866 gtk_entry_set_visibility(GTK_ENTRY(pentry), FALSE);
867 gtk_table_attach_defaults(GTK_TABLE(table), pentry, 1, 2, 1, 2);
868 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
869
870 GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL);
871 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
872 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
873 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_win),
874 GTK_SHADOW_IN);
875
876 GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
877 gtk_text_buffer_set_text(buffer, _("Please add a comment"), -1);
878
879 /* disable ok button until user edited the comment */
880 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
881 GTK_RESPONSE_ACCEPT, FALSE);
882
883 g_signal_connect(G_OBJECT(buffer), "changed",
884 G_CALLBACK(callback_buffer_modified), dialog);
885
886 GtkWidget *view = gtk_text_view_new_with_buffer(buffer);
887 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
888 gtk_text_view_set_editable(GTK_TEXT_VIEW(view), TRUE);
889 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 2 );
890 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 2 );
891
892 g_object_set_data(G_OBJECT(view), "first_click", GINT_TO_POINTER(TRUE));
893 g_signal_connect(G_OBJECT(view), "focus-in-event",
894 G_CALLBACK(cb_focus_in), buffer);
895
896
897 gtk_container_add(GTK_CONTAINER(scrolled_win), view);
898
899 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
900 scrolled_win);
901 gtk_widget_show_all(dialog);
902
903 if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
904 printf("upload cancelled\n");
905 gtk_widget_destroy(dialog);
906 return;
907 }
908
909 printf("clicked ok\n");
910
911 /* retrieve username and password */
912 if(appdata->settings->username)
913 g_free(appdata->settings->username);
914 appdata->settings->username =
915 g_strdup(gtk_entry_get_text(GTK_ENTRY(uentry)));
916
917 if(appdata->settings->password)
918 g_free(appdata->settings->password);
919 appdata->settings->password =
920 g_strdup(gtk_entry_get_text(GTK_ENTRY(pentry)));
921
922 /* osm upload itself also has a gui */
923 osm_upload_context_t *context = g_new0(osm_upload_context_t, 1);
924 context->appdata = appdata;
925 context->osm = osm;
926 context->project = project;
927
928 /* add proxy settings if required */
929 if(appdata->settings)
930 context->proxy = appdata->settings->proxy;
931
932 /* fetch comment from dialog */
933 GtkTextIter start, end;
934 gtk_text_buffer_get_start_iter(buffer, &start);
935 gtk_text_buffer_get_end_iter(buffer, &end);
936 char *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
937 if(text) context->comment = g_strdup(text);
938
939 gtk_widget_destroy(dialog);
940 project_save(GTK_WIDGET(appdata->window), project);
941
942 context->dialog =
943 misc_dialog_new(MISC_DIALOG_LARGE,_("Uploading..."),
944 GTK_WINDOW(appdata->window),
945 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
946
947 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
948 GTK_RESPONSE_CLOSE, FALSE);
949
950 /* ------- main ui elelent is this text view --------------- */
951
952 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
953 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
954 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
955
956 context->log.buffer = gtk_text_buffer_new(NULL);
957
958 context->log.view = gtk_text_view_new_with_buffer(context->log.buffer);
959 gtk_text_view_set_editable(GTK_TEXT_VIEW(context->log.view), FALSE);
960 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(context->log.view), FALSE);
961 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(context->log.view), GTK_WRAP_WORD);
962
963 gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view);
964 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
965 GTK_SHADOW_IN);
966
967 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
968 scrolled_window);
969 gtk_widget_show_all(context->dialog);
970
971 /* server url should not end with a slash */
972 if(project->server[strlen(project->server)-1] == '/') {
973 printf("removing trailing slash\n");
974 project->server[strlen(project->server)-1] = 0;
975 project->dirty = TRUE; /* project needs to be saved */
976 }
977
978 appendf(&context->log, NULL, _("Log generated by %s v%s using API 0.6\n"),
979 PACKAGE, VERSION);
980 appendf(&context->log, NULL, _("User comment: %s\n"), context->comment);
981
982 g_assert(project->server);
983
984 /* check if server name contains string "0.5" and adjust it */
985 if(strstr(project->server, "0.5") != NULL) {
986 strstr(project->server, "0.5")[2] = '6';
987
988 appendf(&context->log, NULL, _("Adjusting server name to v0.6\n"));
989 project->dirty = TRUE; // project needs to be changed
990 }
991
992 appendf(&context->log, NULL, _("Uploading to %s\n"), project->server);
993
994 /* create a new changeset */
995 if(osm_create_changeset(context)) {
996 /* check for dirty entries */
997 appendf(&context->log, NULL, _("Uploading nodes:\n"));
998 osm_upload_nodes(context);
999 appendf(&context->log, NULL, _("Uploading ways:\n"));
1000 osm_upload_ways(context);
1001 appendf(&context->log, NULL, _("Uploading relations:\n"));
1002 osm_upload_relations(context);
1003 appendf(&context->log, NULL, _("Deleting relations:\n"));
1004 osm_delete_relations(context);
1005 appendf(&context->log, NULL, _("Deleting ways:\n"));
1006 osm_delete_ways(context);
1007 appendf(&context->log, NULL, _("Deleting nodes:\n"));
1008 osm_delete_nodes(context);
1009
1010 /* close changeset */
1011 osm_close_changeset(context);
1012 }
1013
1014 appendf(&context->log, NULL, _("Upload done.\n"));
1015
1016 gboolean reload_map = FALSE;
1017 if(project->data_dirty) {
1018 appendf(&context->log, NULL, _("Server data has been modified.\n"));
1019 appendf(&context->log, NULL, _("Downloading updated osm data ...\n"));
1020
1021 if(osm_download(context->dialog, appdata->settings, project)) {
1022 appendf(&context->log, NULL, _("Download successful!\n"));
1023 appendf(&context->log, NULL, _("The map will be reloaded.\n"));
1024 project->data_dirty = FALSE;
1025 reload_map = TRUE;
1026 } else
1027 appendf(&context->log, NULL, _("Download failed!\n"));
1028
1029 project_save(context->dialog, project);
1030
1031 if(reload_map) {
1032 /* this kind of rather brute force reload is useful as the moment */
1033 /* after the upload is a nice moment to bring everything in sync again. */
1034 /* we basically restart the entire map with fresh data from the server */
1035 /* and the diff will hopefully be empty (if the upload was successful) */
1036
1037 appendf(&context->log, NULL, _("Reloading map ...\n"));
1038
1039 if(!diff_is_clean(appdata->osm, FALSE)) {
1040 appendf(&context->log, COLOR_ERR, _("*** DIFF IS NOT CLEAN ***\n"));
1041 appendf(&context->log, COLOR_ERR, _("Something went wrong during upload,\n"));
1042 appendf(&context->log, COLOR_ERR, _("proceed with care!\n"));
1043 }
1044
1045 /* redraw the entire map by destroying all map items and redrawing them */
1046 appendf(&context->log, NULL, _("Cleaning up ...\n"));
1047 diff_save(appdata->project, appdata->osm);
1048 map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
1049 osm_free(&appdata->icon, appdata->osm);
1050
1051 appendf(&context->log, NULL, _("Loading OSM ...\n"));
1052 appdata->osm = osm_parse(appdata->project->path, appdata->project->osm);
1053 appendf(&context->log, NULL, _("Applying diff ...\n"));
1054 diff_restore(appdata, appdata->project, appdata->osm);
1055 appendf(&context->log, NULL, _("Painting ...\n"));
1056 map_paint(appdata);
1057 appendf(&context->log, NULL, _("Done!\n"));
1058 }
1059 }
1060
1061 /* tell the user that he can stop waiting ... */
1062 appendf(&context->log, NULL, _("Process finished.\n"));
1063
1064 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
1065 GTK_RESPONSE_CLOSE, TRUE);
1066
1067 gtk_dialog_run(GTK_DIALOG(context->dialog));
1068 gtk_widget_destroy(context->dialog);
1069
1070 if(context->comment) g_free(context->comment);
1071 g_free(context);
1072 }
1073