OSDN Git Service

Merge branch 'For2.2.2-Refactoring' of git.osdn.net:/gitroot/hengband/hengband into...
[hengband/hengband.git] / src / report.c
1 /*!
2  * @file report.c
3  * @brief スコアサーバ転送機能の実装
4  * @date 2014/07/14
5  * @author Hengband Team
6  */
7
8 #include "angband.h"
9 #include "core.h"
10 #include "inet.h"
11 #include "dungeon.h"
12
13 #include "player-personality.h"
14 #include "character-dump.h"
15 #include "world.h"
16 #include "term.h"
17
18 #ifdef WORLD_SCORE
19
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <ctype.h>
23 #include <string.h>
24
25 #if defined(WINDOWS)
26 #include <winsock.h>
27 #else
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <sys/time.h>
33
34 #include <setjmp.h>
35 #include <signal.h>
36 #endif
37
38 concptr screen_dump = NULL;
39
40 /*
41  * internet resource value
42  */
43 #define HTTP_PROXY ""                   /*!< デフォルトのプロキシURL / Default proxy url */
44 #define HTTP_PROXY_PORT 0               /*!< デフォルトのプロキシポート / Default proxy port */
45 #define HTTP_TIMEOUT    20              /*!< デフォルトのタイムアウト時間(秒) / Timeout length (second) */
46 #define SCORE_SERVER "hengband.osdn.jp" /*!< デフォルトのスコアサーバURL / Default score server url */
47 #define SCORE_PORT 80                   /*!< デフォルトのスコアサーバポート / Default score server port */
48
49 #ifdef JP
50 #define SCORE_PATH "http://hengband.osdn.jp/score/register_score.php" /*!< スコア開示URL */
51 #else
52 #define SCORE_PATH "http://moon.kmc.gr.jp/hengband/hengscore-en/score.cgi" /*!< スコア開示URL */
53 #endif
54
55  /*
56   * simple buffer library
57   */
58 typedef struct {
59         size_t max_size;
60         size_t size;
61         char *data;
62 } BUF;
63
64 #define BUFSIZE (65536) /*!< スコアサーバ転送バッファサイズ */
65
66 /*!
67  * @brief 転送用バッファの確保
68  * @return 確保したバッファの参照ポインタ
69  */
70 static BUF* buf_new(void)
71 {
72         BUF *p;
73         p = malloc(sizeof(BUF));
74         if (!p) return NULL;
75
76         p->size = 0;
77         p->max_size = BUFSIZE;
78         p->data = malloc(BUFSIZE);
79         if (!p->data)
80         {
81                 free(p);
82                 return NULL;
83         }
84
85         return p;
86 }
87
88
89 /*!
90  * @brief 転送用バッファの解放
91  * @param b 解放するバッファの参照ポインタ
92  */
93 static void buf_delete(BUF *b)
94 {
95         free(b->data);
96         free(b);
97 }
98
99
100 /*!
101  * @brief 転送用バッファにデータを追加する
102  * @param buf 追加先バッファの参照ポインタ
103  * @param data 追加元データ
104  * @param size 追加サイズ
105  * @return 追加後のバッファ容量
106  */
107 static int buf_append(BUF *buf, concptr data, size_t size)
108 {
109         while (buf->size + size > buf->max_size)
110         {
111                 char *tmp;
112                 if ((tmp = malloc(buf->max_size * 2)) == NULL) return -1;
113
114                 memcpy(tmp, buf->data, buf->max_size);
115                 free(buf->data);
116
117                 buf->data = tmp;
118
119                 buf->max_size *= 2;
120         }
121         memcpy(buf->data + buf->size, data, size);
122         buf->size += size;
123
124         return buf->size;
125 }
126
127
128 /*!
129  * @brief 転送用バッファにフォーマット指定した文字列データを追加する
130  * @param buf 追加先バッファの参照ポインタ
131  * @param fmt 文字列フォーマット
132  * @return 追加後のバッファ容量
133  */
134 static int buf_sprintf(BUF *buf, concptr fmt, ...)
135 {
136         int             ret;
137         char    tmpbuf[8192];
138         va_list ap;
139
140         va_start(ap, fmt);
141 #if defined(HAVE_VSNPRINTF)
142         ret = vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
143 #else
144         ret = vsprintf(tmpbuf, fmt, ap);
145 #endif
146         va_end(ap);
147
148         if (ret < 0) return -1;
149
150 #if ('\r' == 0x0a && '\n' == 0x0d)
151         {
152                 /*
153                  * Originally '\r'= CR (= 0x0d) and '\n'= LF (= 0x0a)
154                  * But for MPW (Macintosh Programers Workbench), these
155                  * are reversed so that '\r'=LF and '\n'=CR unless the
156                  * -noMapCR option is not defined.
157                  *
158                  * We need to swap back these here since the score
159                  * dump text should be written using LF as the end of
160                  * line.
161                  */
162                 char *ptr;
163                 for (ptr = tmpbuf; *ptr; ptr++)
164                 {
165                         if (0x0d == *ptr) *ptr = 0x0a;
166                 }
167         }
168 #endif
169
170         ret = buf_append(buf, tmpbuf, strlen(tmpbuf));
171
172         return ret;
173 }
174
175
176 /*!
177  * @brief HTTPによるダンプ内容伝送
178  * @param sd ソケットID
179  * @param url 伝送先URL
180  * @param buf 伝送内容バッファ
181  * @return なし
182  */
183 static bool http_post(int sd, concptr url, BUF *buf)
184 {
185         BUF *output;
186         char response_buf[1024] = "";
187         concptr HTTP_RESPONSE_CODE_OK = "HTTP/1.1 200 OK";
188
189         output = buf_new();
190         buf_sprintf(output, "POST %s HTTP/1.0\r\n", url);
191         buf_sprintf(output, "User-Agent: Hengband %d.%d.%d\r\n",
192                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
193
194         buf_sprintf(output, "Content-Length: %d\r\n", buf->size);
195         buf_sprintf(output, "Content-Encoding: binary\r\n");
196 #ifdef JP
197 #ifdef SJIS
198         buf_sprintf(output, "Content-Type: text/plain; charset=SHIFT_JIS\r\n");
199 #endif
200 #ifdef EUC
201         buf_sprintf(output, "Content-Type: text/plain; charset=EUC-JP\r\n");
202 #endif
203 #else
204         buf_sprintf(output, "Content-Type: text/plain; charset=ASCII\r\n");
205 #endif
206         buf_sprintf(output, "\r\n");
207         buf_append(output, buf->data, buf->size);
208
209         soc_write(sd, output->data, output->size);
210
211         soc_read(sd, response_buf, sizeof(response_buf));
212
213         return strncmp(response_buf, HTTP_RESPONSE_CODE_OK, strlen(HTTP_RESPONSE_CODE_OK)) == 0;
214 }
215
216
217 /*!
218  * @brief キャラクタダンプを作って BUFに保存
219  * @param creature_ptr プレーヤーへの参照ポインタ
220  * @param dumpbuf 伝送内容バッファ
221  * @return エラーコード
222  */
223 static errr make_dump(player_type *creature_ptr, BUF* dumpbuf, void(*update_playtime)(void), display_player_pf display_player, map_name_pf map_name)
224 {
225         char            buf[1024];
226         FILE *fff;
227         GAME_TEXT file_name[1024];
228
229         /* Open a new file */
230         fff = my_fopen_temp(file_name, 1024);
231         if (!fff)
232         {
233 #ifdef JP
234                 msg_format("一時ファイル %s を作成できませんでした。", file_name);
235 #else
236                 msg_format("Failed to create temporary file %s.", file_name);
237 #endif
238                 msg_print(NULL);
239                 return 1;
240         }
241
242         /* 一旦一時ファイルを作る。通常のダンプ出力と共通化するため。 */
243         make_character_dump(creature_ptr, fff, update_playtime, display_player, map_name);
244         my_fclose(fff);
245
246         /* Open for read */
247         fff = my_fopen(file_name, "r");
248
249         while (fgets(buf, 1024, fff))
250         {
251                 (void)buf_sprintf(dumpbuf, "%s", buf);
252         }
253         my_fclose(fff);
254         fd_kill(file_name);
255
256         /* Success */
257         return 0;
258 }
259
260
261 /*!
262  * @brief スクリーンダンプを作成する/ Make screen dump to buffer
263  * @return 作成したスクリーンダンプの参照ポインタ
264  */
265 concptr make_screen_dump(player_type *creature_ptr)
266 {
267         static concptr html_head[] = {
268                 "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n",
269                 "<pre>",
270                 0,
271         };
272         static concptr html_foot[] = {
273                 "</pre>\n",
274                 "</body>\n</html>\n",
275                 0,
276         };
277
278         int wid, hgt;
279         Term_get_size(&wid, &hgt);
280
281         /* Alloc buffer */
282         BUF *screen_buf;
283         screen_buf = buf_new();
284         if (screen_buf == NULL) return (NULL);
285
286         bool old_use_graphics = use_graphics;
287         if (old_use_graphics)
288         {
289                 /* Clear -more- prompt first */
290                 msg_print(NULL);
291
292                 use_graphics = FALSE;
293                 reset_visuals(creature_ptr);
294
295                 creature_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
296                 handle_stuff(creature_ptr);
297         }
298
299         for (int i = 0; html_head[i]; i++)
300                 buf_sprintf(screen_buf, html_head[i]);
301
302         /* Dump the screen */
303         for (int y = 0; y < hgt; y++)
304         {
305                 /* Start the row */
306                 if (y != 0)
307                         buf_sprintf(screen_buf, "\n");
308
309                 /* Dump each row */
310                 TERM_COLOR a = 0, old_a = 0;
311                 SYMBOL_CODE c = ' ';
312                 for (int x = 0; x < wid - 1; x++)
313                 {
314                         int rv, gv, bv;
315                         concptr cc = NULL;
316                         /* Get the attr/char */
317                         (void)(Term_what(x, y, &a, &c));
318
319                         switch (c)
320                         {
321                         case '&': cc = "&amp;"; break;
322                         case '<': cc = "&lt;"; break;
323                         case '>': cc = "&gt;"; break;
324                         case '"': cc = "&quot;"; break;
325                         case '\'': cc = "&#39;"; break;
326 #ifdef WINDOWS
327                         case 0x1f: c = '.'; break;
328                         case 0x7f: c = (a == 0x09) ? '%' : '#'; break;
329 #endif
330                         }
331
332                         a = a & 0x0F;
333                         if ((y == 0 && x == 0) || a != old_a) {
334                                 rv = angband_color_table[a][1];
335                                 gv = angband_color_table[a][2];
336                                 bv = angband_color_table[a][3];
337                                 buf_sprintf(screen_buf, "%s<font color=\"#%02x%02x%02x\">",
338                                         ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
339                                 old_a = a;
340                         }
341
342                         if (cc)
343                                 buf_sprintf(screen_buf, "%s", cc);
344                         else
345                                 buf_sprintf(screen_buf, "%c", c);
346                 }
347         }
348
349         buf_sprintf(screen_buf, "</font>");
350
351         for (int i = 0; html_foot[i]; i++)
352                 buf_sprintf(screen_buf, html_foot[i]);
353
354         /* Screen dump size is too big ? */
355         concptr ret;
356         if (screen_buf->size + 1 > SCREEN_BUF_MAX_SIZE)
357         {
358                 ret = NULL;
359         }
360         else
361         {
362                 /* Terminate string */
363                 buf_append(screen_buf, "", 1);
364
365                 ret = string_make(screen_buf->data);
366         }
367
368         /* Free buffer */
369         buf_delete(screen_buf);
370
371         if (!old_use_graphics) return ret;
372
373         use_graphics = TRUE;
374         reset_visuals(creature_ptr);
375
376         creature_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
377         handle_stuff(creature_ptr);
378         return ret;
379 }
380
381
382 /*!
383  * todo メッセージは言語選択の関数マクロで何とかならんか?
384  * @brief スコア転送処理のメインルーチン
385  * @param creature_ptr プレーヤーへの参照ポインタ
386  * @return 正常終了の時0、異常があったら1
387  */
388 errr report_score(player_type *creature_ptr, void(*update_playtime)(void), display_player_pf display_player, map_name_pf map_name)
389 {
390 #ifdef WINDOWS
391         WSADATA wsaData;
392         WORD wVersionRequested = (WORD)((1) | (1 << 8));
393 #endif
394
395         BUF *score;
396         score = buf_new();
397
398         char seikakutmp[128];
399 #ifdef JP
400         sprintf(seikakutmp, "%s%s", ap_ptr->title, (ap_ptr->no ? "の" : ""));
401 #else
402         sprintf(seikakutmp, "%s ", ap_ptr->title);
403 #endif
404
405         buf_sprintf(score, "name: %s\n", creature_ptr->name);
406 #ifdef JP
407         buf_sprintf(score, "version: 変愚蛮怒 %d.%d.%d\n",
408                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
409 #else
410         buf_sprintf(score, "version: Hengband %d.%d.%d\n",
411                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
412 #endif
413         buf_sprintf(score, "score: %d\n", calc_score(creature_ptr));
414         buf_sprintf(score, "level: %d\n", creature_ptr->lev);
415         buf_sprintf(score, "depth: %d\n", creature_ptr->current_floor_ptr->dun_level);
416         buf_sprintf(score, "maxlv: %d\n", creature_ptr->max_plv);
417         buf_sprintf(score, "maxdp: %d\n", max_dlv[DUNGEON_ANGBAND]);
418         buf_sprintf(score, "au: %d\n", creature_ptr->au);
419         buf_sprintf(score, "turns: %d\n", turn_real(creature_ptr, current_world_ptr->game_turn));
420         buf_sprintf(score, "sex: %d\n", creature_ptr->psex);
421         buf_sprintf(score, "race: %s\n", rp_ptr->title);
422         buf_sprintf(score, "class: %s\n", cp_ptr->title);
423         buf_sprintf(score, "seikaku: %s\n", seikakutmp);
424         buf_sprintf(score, "realm1: %s\n", realm_names[creature_ptr->realm1]);
425         buf_sprintf(score, "realm2: %s\n", realm_names[creature_ptr->realm2]);
426         buf_sprintf(score, "killer: %s\n", creature_ptr->died_from);
427         buf_sprintf(score, "-----charcter dump-----\n");
428
429         make_dump(creature_ptr, score, update_playtime, display_player, map_name);
430
431         if (screen_dump)
432         {
433                 buf_sprintf(score, "-----screen shot-----\n");
434                 buf_append(score, screen_dump, strlen(screen_dump));
435         }
436
437 #ifdef WINDOWS
438         if (WSAStartup(wVersionRequested, &wsaData))
439         {
440                 msg_print("Report: WSAStartup failed.");
441 #ifdef WINDOWS
442                 WSACleanup();
443 #endif
444                 return 1;
445         }
446 #endif
447
448         Term_clear();
449
450         int sd;
451         while (TRUE)
452         {
453                 char buff[160];
454 #ifdef JP
455                 prt("接続中...", 0, 0);
456 #else
457                 prt("connecting...", 0, 0);
458 #endif
459                 Term_fresh();
460
461                 /* プロキシを設定する */
462                 set_proxy(HTTP_PROXY, HTTP_PROXY_PORT);
463
464                 /* Connect to the score server */
465                 sd = connect_server(HTTP_TIMEOUT, SCORE_SERVER, SCORE_PORT);
466
467
468                 if (sd < 0) {
469 #ifdef JP
470                         sprintf(buff, "スコア・サーバへの接続に失敗しました。(%s)", soc_err());
471 #else
472                         sprintf(buff, "Failed to connect to the score server.(%s)", soc_err());
473 #endif
474                         prt(buff, 0, 0);
475                         (void)inkey();
476
477 #ifdef JP
478                         if (!get_check_strict("もう一度接続を試みますか? ", CHECK_NO_HISTORY))
479 #else
480                         if (!get_check_strict("Try again? ", CHECK_NO_HISTORY))
481 #endif
482                         {
483 #ifdef WINDOWS
484                                 WSACleanup();
485 #endif
486                                 return 1;
487                         }
488
489                         continue;
490                 }
491
492 #ifdef JP
493                 prt("スコア送信中...", 0, 0);
494 #else
495                 prt("Sending the score...", 0, 0);
496 #endif
497                 Term_fresh();
498
499                 if (!http_post(sd, SCORE_PATH, score)) {
500                         disconnect_server(sd);
501 #ifdef JP
502                         sprintf(buff, "スコア・サーバへの送信に失敗しました。");
503 #else
504                         sprintf(buff, "Failed to send to the score server.");
505 #endif
506                         prt(buff, 0, 0);
507                         (void)inkey();
508
509 #ifdef JP
510                         if (!get_check_strict("もう一度接続を試みますか? ", CHECK_NO_HISTORY))
511 #else
512                         if (!get_check_strict("Try again? ", CHECK_NO_HISTORY))
513 #endif
514                         {
515 #ifdef WINDOWS
516                                 WSACleanup();
517 #endif
518                                 return 1;
519                         }
520
521                         continue;
522                 }
523
524                 disconnect_server(sd);
525                 break;
526         }
527
528 #ifdef WINDOWS
529         WSACleanup();
530 #endif
531
532         return 0;
533 }
534 #endif /* WORLD_SCORE */