user.c (12598B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "config.h" 21 22#include "encode-jpeg.h" 23#include "encode-png.h" 24#include "encode-webp.h" 25#include "guacamole/mem.h" 26#include "guacamole/client.h" 27#include "guacamole/object.h" 28#include "guacamole/pool.h" 29#include "guacamole/protocol.h" 30#include "guacamole/socket.h" 31#include "guacamole/stream.h" 32#include "guacamole/string.h" 33#include "guacamole/timestamp.h" 34#include "guacamole/user.h" 35#include "id.h" 36#include "user-handlers.h" 37 38#include <errno.h> 39#include <limits.h> 40#include <stdlib.h> 41#include <string.h> 42 43guac_user* guac_user_alloc() { 44 45 guac_user* user = guac_mem_zalloc(sizeof(guac_user)); 46 int i; 47 48 /* Generate ID */ 49 user->user_id = guac_generate_id(GUAC_USER_ID_PREFIX); 50 if (user->user_id == NULL) { 51 guac_mem_free(user); 52 return NULL; 53 } 54 55 user->last_received_timestamp = guac_timestamp_current(); 56 user->last_frame_duration = 0; 57 user->processing_lag = 0; 58 user->active = 1; 59 60 /* Allocate stream pool */ 61 user->__stream_pool = guac_pool_alloc(0); 62 63 /* Initialze streams */ 64 user->__input_streams = guac_mem_alloc(sizeof(guac_stream), GUAC_USER_MAX_STREAMS); 65 user->__output_streams = guac_mem_alloc(sizeof(guac_stream), GUAC_USER_MAX_STREAMS); 66 67 for (i=0; i<GUAC_USER_MAX_STREAMS; i++) { 68 user->__input_streams[i].index = GUAC_USER_CLOSED_STREAM_INDEX; 69 user->__output_streams[i].index = GUAC_USER_CLOSED_STREAM_INDEX; 70 } 71 72 /* Allocate object pool */ 73 user->__object_pool = guac_pool_alloc(0); 74 75 /* Initialize objects */ 76 user->__objects = guac_mem_alloc(sizeof(guac_object), GUAC_USER_MAX_OBJECTS); 77 for (i=0; i<GUAC_USER_MAX_OBJECTS; i++) 78 user->__objects[i].index = GUAC_USER_UNDEFINED_OBJECT_INDEX; 79 80 return user; 81 82} 83 84void guac_user_free(guac_user* user) { 85 86 /* Free streams */ 87 guac_mem_free(user->__input_streams); 88 guac_mem_free(user->__output_streams); 89 90 /* Free stream pool */ 91 guac_pool_free(user->__stream_pool); 92 93 /* Free objects */ 94 guac_mem_free(user->__objects); 95 96 /* Free object pool */ 97 guac_pool_free(user->__object_pool); 98 99 /* Clean up user */ 100 guac_mem_free(user->user_id); 101 guac_mem_free(user); 102 103} 104 105guac_stream* guac_user_alloc_stream(guac_user* user) { 106 107 guac_stream* allocd_stream; 108 int stream_index; 109 110 /* Refuse to allocate beyond maximum */ 111 if (user->__stream_pool->active == GUAC_USER_MAX_STREAMS) 112 return NULL; 113 114 /* Allocate stream */ 115 stream_index = guac_pool_next_int(user->__stream_pool); 116 117 /* Initialize stream with even index (odd indices are client-level) */ 118 allocd_stream = &(user->__output_streams[stream_index]); 119 allocd_stream->index = stream_index * 2; 120 allocd_stream->data = NULL; 121 allocd_stream->ack_handler = NULL; 122 allocd_stream->blob_handler = NULL; 123 allocd_stream->end_handler = NULL; 124 125 return allocd_stream; 126 127} 128 129void guac_user_free_stream(guac_user* user, guac_stream* stream) { 130 131 /* Release index to pool */ 132 guac_pool_free_int(user->__stream_pool, stream->index / 2); 133 134 /* Mark stream as closed */ 135 stream->index = GUAC_USER_CLOSED_STREAM_INDEX; 136 137} 138 139guac_object* guac_user_alloc_object(guac_user* user) { 140 141 guac_object* allocd_object; 142 int object_index; 143 144 /* Refuse to allocate beyond maximum */ 145 if (user->__object_pool->active == GUAC_USER_MAX_OBJECTS) 146 return NULL; 147 148 /* Allocate object */ 149 object_index = guac_pool_next_int(user->__object_pool); 150 151 /* Initialize object */ 152 allocd_object = &(user->__objects[object_index]); 153 allocd_object->index = object_index; 154 allocd_object->data = NULL; 155 allocd_object->get_handler = NULL; 156 allocd_object->put_handler = NULL; 157 158 return allocd_object; 159 160} 161 162void guac_user_free_object(guac_user* user, guac_object* object) { 163 164 /* Release index to pool */ 165 guac_pool_free_int(user->__object_pool, object->index); 166 167 /* Mark object as undefined */ 168 object->index = GUAC_USER_UNDEFINED_OBJECT_INDEX; 169 170} 171 172int guac_user_handle_instruction(guac_user* user, const char* opcode, int argc, char** argv) { 173 174 return __guac_user_call_opcode_handler(__guac_instruction_handler_map, 175 user, opcode, argc, argv); 176 177} 178 179void guac_user_stop(guac_user* user) { 180 user->active = 0; 181} 182 183void vguac_user_abort(guac_user* user, guac_protocol_status status, 184 const char* format, va_list ap) { 185 186 /* Only relevant if user is active */ 187 if (user->active) { 188 189 /* Log detail of error */ 190 vguac_user_log(user, GUAC_LOG_ERROR, format, ap); 191 192 /* Send error immediately, limit information given */ 193 guac_protocol_send_error(user->socket, "Aborted. See logs.", status); 194 guac_socket_flush(user->socket); 195 196 /* Stop user */ 197 guac_user_stop(user); 198 199 } 200 201} 202 203void guac_user_abort(guac_user* user, guac_protocol_status status, 204 const char* format, ...) { 205 206 va_list args; 207 va_start(args, format); 208 209 vguac_user_abort(user, status, format, args); 210 211 va_end(args); 212 213} 214 215void vguac_user_log(guac_user* user, guac_client_log_level level, 216 const char* format, va_list ap) { 217 218 vguac_client_log(user->client, level, format, ap); 219 220} 221 222void guac_user_log(guac_user* user, guac_client_log_level level, 223 const char* format, ...) { 224 225 va_list args; 226 va_start(args, format); 227 228 vguac_client_log(user->client, level, format, args); 229 230 va_end(args); 231 232} 233 234void guac_user_stream_argv(guac_user* user, guac_socket* socket, 235 const char* mimetype, const char* name, const char* value) { 236 237 /* Allocate new stream for argument value */ 238 guac_stream* stream = guac_user_alloc_stream(user); 239 240 /* Declare stream as containing connection parameter data */ 241 guac_protocol_send_argv(socket, stream, mimetype, name); 242 243 /* Write parameter data */ 244 guac_protocol_send_blobs(socket, stream, value, strlen(value)); 245 246 /* Terminate stream */ 247 guac_protocol_send_end(socket, stream); 248 249 /* Free allocated stream */ 250 guac_user_free_stream(user, stream); 251 252} 253 254void guac_user_stream_png(guac_user* user, guac_socket* socket, 255 guac_composite_mode mode, const guac_layer* layer, int x, int y, 256 cairo_surface_t* surface) { 257 258 /* Allocate new stream for image */ 259 guac_stream* stream = guac_user_alloc_stream(user); 260 261 /* Declare stream as containing image data */ 262 guac_protocol_send_img(socket, stream, mode, layer, "image/png", x, y); 263 264 /* Write PNG data */ 265 guac_png_write(socket, stream, surface); 266 267 /* Terminate stream */ 268 guac_protocol_send_end(socket, stream); 269 270 /* Free allocated stream */ 271 guac_user_free_stream(user, stream); 272 273} 274 275void guac_user_stream_jpeg(guac_user* user, guac_socket* socket, 276 guac_composite_mode mode, const guac_layer* layer, int x, int y, 277 cairo_surface_t* surface, int quality) { 278 279 /* Allocate new stream for image */ 280 guac_stream* stream = guac_user_alloc_stream(user); 281 282 /* Declare stream as containing image data */ 283 guac_protocol_send_img(socket, stream, mode, layer, "image/jpeg", x, y); 284 285 /* Write JPEG data */ 286 guac_jpeg_write(socket, stream, surface, quality); 287 288 /* Terminate stream */ 289 guac_protocol_send_end(socket, stream); 290 291 /* Free allocated stream */ 292 guac_user_free_stream(user, stream); 293 294} 295 296void guac_user_stream_webp(guac_user* user, guac_socket* socket, 297 guac_composite_mode mode, const guac_layer* layer, int x, int y, 298 cairo_surface_t* surface, int quality, int lossless) { 299 300#ifdef ENABLE_WEBP 301 /* Allocate new stream for image */ 302 guac_stream* stream = guac_user_alloc_stream(user); 303 304 /* Declare stream as containing image data */ 305 guac_protocol_send_img(socket, stream, mode, layer, "image/webp", x, y); 306 307 /* Write WebP data */ 308 guac_webp_write(socket, stream, surface, quality, lossless); 309 310 /* Terminate stream */ 311 guac_protocol_send_end(socket, stream); 312 313 /* Free allocated stream */ 314 guac_user_free_stream(user, stream); 315#else 316 /* Do nothing if WebP support is not built in */ 317#endif 318 319} 320 321int guac_user_supports_msg(guac_user* user) { 322 323 if (user == NULL) 324 return 0; 325 326 return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_5_0); 327 328} 329 330int guac_user_supports_required(guac_user* user) { 331 332 if (user == NULL) 333 return 0; 334 335 return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_3_0); 336 337} 338 339int guac_user_supports_webp(guac_user* user) { 340 341#ifdef ENABLE_WEBP 342 const char** mimetype = user->info.image_mimetypes; 343 344 /* Search for WebP mimetype in list of supported image mimetypes */ 345 while (*mimetype != NULL) { 346 347 /* If WebP mimetype found, no need to search further */ 348 if (strcmp(*mimetype, "image/webp") == 0) 349 return 1; 350 351 /* Next mimetype */ 352 mimetype++; 353 354 } 355 356 /* User does not support WebP */ 357 return 0; 358#else 359 /* Support for WebP is completely absent */ 360 return 0; 361#endif 362 363} 364 365char* guac_user_parse_args_string(guac_user* user, const char** arg_names, 366 const char** argv, int index, const char* default_value) { 367 368 /* Pull parameter value from argv */ 369 const char* value = argv[index]; 370 371 /* Use default value if blank */ 372 if (value[0] == 0) { 373 374 /* NULL is a completely legal default value */ 375 if (default_value == NULL) 376 return NULL; 377 378 /* Log use of default */ 379 guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using " 380 "default value of \"%s\".", arg_names[index], default_value); 381 382 return guac_strdup(default_value); 383 384 } 385 386 /* Otherwise use provided value */ 387 return guac_strdup(value); 388 389} 390 391int guac_user_parse_args_int(guac_user* user, const char** arg_names, 392 const char** argv, int index, int default_value) { 393 394 char* parse_end; 395 long parsed_value; 396 397 /* Pull parameter value from argv */ 398 const char* value = argv[index]; 399 400 /* Use default value if blank */ 401 if (value[0] == 0) { 402 403 /* Log use of default */ 404 guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using " 405 "default value of %i.", arg_names[index], default_value); 406 407 return default_value; 408 409 } 410 411 /* Parse value, checking for errors */ 412 errno = 0; 413 parsed_value = strtol(value, &parse_end, 10); 414 415 /* Ensure parsed value is within the legal range of an int */ 416 if (parsed_value < INT_MIN || parsed_value > INT_MAX) 417 errno = ERANGE; 418 419 /* Resort to default if input is invalid */ 420 if (errno != 0 || *parse_end != '\0') { 421 422 /* Log use of default */ 423 guac_user_log(user, GUAC_LOG_WARNING, "Specified value \"%s\" for " 424 "parameter \"%s\" is not a valid integer. Using default value " 425 "of %i.", value, arg_names[index], default_value); 426 427 return default_value; 428 429 } 430 431 /* Parsed successfully */ 432 return parsed_value; 433 434} 435 436int guac_user_parse_args_boolean(guac_user* user, const char** arg_names, 437 const char** argv, int index, int default_value) { 438 439 /* Pull parameter value from argv */ 440 const char* value = argv[index]; 441 442 /* Use default value if blank */ 443 if (value[0] == 0) { 444 445 /* Log use of default */ 446 guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using " 447 "default value of %i.", arg_names[index], default_value); 448 449 return default_value; 450 451 } 452 453 /* Parse string "true" as true */ 454 if (strcmp(value, "true") == 0) 455 return 1; 456 457 /* Parse string "false" as false */ 458 if (strcmp(value, "false") == 0) 459 return 0; 460 461 /* All other values are invalid */ 462 guac_user_log(user, GUAC_LOG_WARNING, "Parameter \"%s\" must be either " 463 "\"true\" or \"false\". Using default value.", arg_names[index]); 464 465 return default_value; 466 467} 468