1 /* $Id$ */
2 
3 /*
4  *  (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU Lesser General Public License Version
8  *  2.1 as published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
18  *  USA.
19  */
20 
21 /**
22  * \file protobuf.c
23  *
24  * \brief Funkcje pomocnicze do obsługi formatu protocol buffers
25  */
26 
27 #include "protobuf.h"
28 
29 #define GG_PROTOBUFF_UIN_MAXLEN 15
30 struct _gg_protobuf_uin_buff
31 {
32 	char data[GG_PROTOBUFF_UIN_MAXLEN + 1 + 2];
33 };
34 
gg_protobuf_expected(struct gg_session * gs,const char * field_name,uint32_t value,uint32_t expected)35 void gg_protobuf_expected(struct gg_session *gs, const char *field_name,
36 	uint32_t value, uint32_t expected)
37 {
38 	if (value == expected) {
39 		return;
40 	}
41 	gg_debug_session(gs, GG_DEBUG_WARNING, "// gg_packet: field %s was "
42 		"expected to be %#x, but its value was %#x\n",
43 		field_name, expected, value);
44 }
45 
gg_protobuf_valid_chknull(struct gg_session * gs,const char * msg_name,int isNull)46 int gg_protobuf_valid_chknull(struct gg_session *gs, const char *msg_name,
47 	int isNull)
48 {
49 	if (isNull) {
50 		gg_debug_session(gs, GG_DEBUG_ERROR, "// gg_protobuf: couldn't "
51 			"unpack %s message\n", msg_name);
52 	}
53 	return !isNull;
54 }
55 
gg_protobuf_valid_chkunknown(struct gg_session * gs,const char * msg_name,ProtobufCMessage * base)56 int gg_protobuf_valid_chkunknown(struct gg_session *gs, const char *msg_name,
57 	ProtobufCMessage *base)
58 {
59 	if (base->n_unknown_fields > 0) {
60 		gg_debug_session(gs, GG_DEBUG_WARNING, "// gg_protobuf: message"
61 		" %s had %d unknown field(s)\n",
62 		msg_name, base->n_unknown_fields);
63 	}
64 	return 1;
65 }
66 
gg_protobuf_send_ex(struct gg_session * gs,struct gg_event * ge,int type,void * msg,gg_protobuf_size_cb_t size_cb,gg_protobuf_pack_cb_t pack_cb)67 int gg_protobuf_send_ex(struct gg_session *gs, struct gg_event *ge, int type,
68 	void *msg, gg_protobuf_size_cb_t size_cb,
69 	gg_protobuf_pack_cb_t pack_cb)
70 {
71 	void *buffer;
72 	size_t len;
73 	int succ = 1;
74 	enum gg_failure_t failure;
75 
76 	len = size_cb(msg);
77 	buffer = malloc(len);
78 	if (buffer == NULL) {
79 		gg_debug_session(gs, GG_DEBUG_ERROR, "// gg_protobuf_send: out "
80 			"of memory - tried to allocate %" GG_SIZE_FMT
81 			" bytes for %#x packet\n", len, type);
82 		succ = 0;
83 		failure = GG_FAILURE_INTERNAL;
84 	} else {
85 		pack_cb(msg, buffer);
86 		succ = (-1 != gg_send_packet(gs, type, buffer, len, NULL));
87 		free(buffer);
88 		buffer = NULL;
89 		if (!succ) {
90 			failure = GG_FAILURE_WRITING;
91 			gg_debug_session(gs, GG_DEBUG_ERROR,
92 				"// gg_protobuf_send: sending packet %#x "
93 				"failed. (errno=%d, %s)\n", type, errno,
94 				strerror(errno));
95 		}
96 	}
97 
98 	if (!succ) {
99 		gg_connection_failure(gs, ge, failure);
100 	}
101 
102 	return succ;
103 }
104 
gg_protobuf_set_uin(ProtobufCBinaryData * dst,uin_t uin,gg_protobuf_uin_buff_t * buff)105 void gg_protobuf_set_uin(ProtobufCBinaryData *dst, uin_t uin, gg_protobuf_uin_buff_t *buff)
106 {
107 	char *uin_str;
108 	int uin_len;
109 	static gg_protobuf_uin_buff_t static_buffer;
110 
111 	if (buff == NULL) {
112 		buff = &static_buffer;
113 	}
114 
115 	uin_str = buff->data + 2;
116 	uin_len = snprintf(uin_str, GG_PROTOBUFF_UIN_MAXLEN + 1, "%u", uin);
117 
118 	buff->data[0] = 0x01; /* magic: 0x00 lub 0x01 */
119 	buff->data[1] = uin_len;
120 
121 	dst->len = uin_len + 2;
122 	dst->data = (uint8_t*)&buff->data;
123 }
124 
gg_protobuf_get_uin(ProtobufCBinaryData uin_data)125 uin_t gg_protobuf_get_uin(ProtobufCBinaryData uin_data)
126 {
127 	uint8_t magic;
128 	size_t uin_len;
129 	const char *uin_str;
130 	uin_t uin;
131 
132 	magic = (uin_data.len > 0) ? uin_data.data[0] : 0;
133 	uin_len = (uin_data.len > 1) ? uin_data.data[1] : 0;
134 
135 	if (uin_data.len != uin_len + 2 || uin_len > 10) {
136 		gg_debug(GG_DEBUG_ERROR, "// gg_protobuf_get_uin: "
137 			"invalid length\n");
138 		return 0;
139 	}
140 	if (magic != 0) {
141 		gg_debug(GG_DEBUG_WARNING, "// gg_protobuf_get_uin: "
142 			"unexpected magic value=%#x\n", magic);
143 	}
144 
145 	uin_str = (char*)(uin_data.data + 2);
146 	uin = gg_str_to_uin(uin_str, uin_len);
147 
148 	if (uin == 0) {
149 		gg_debug(GG_DEBUG_ERROR, "// gg_protobuf_get_uin: "
150 			"invalid uin\n");
151 	}
152 	return uin;
153 }
154