Beispiel #1
0
static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set)
{
	irc_dcc_session_t * dcc;

	/*
	 * We need to use such a complex scheme here, because on every callback
     * a number of DCC sessions could be destroyed.
     */
	libirc_mutex_lock (&ircsession->mutex_dcc);

	for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next )
	{
		if ( dcc->state == LIBIRC_STATE_LISTENING
		&& FD_ISSET (dcc->sock, in_set) )
		{
			socklen_t len = sizeof(dcc->remote_addr);
			int nsock, err = 0;

			// New connection is available; accept it.
			if ( socket_accept (&dcc->sock, &nsock, (struct sockaddr *) &dcc->remote_addr, &len) )
				err = LIBIRC_ERR_ACCEPT;

			// On success, change the active socket and change the state
			if ( err == 0 )
			{
				// close the listen socket, and replace it by a newly 
				// accepted
				socket_close (&dcc->sock);
				dcc->sock = nsock;
				dcc->state = LIBIRC_STATE_CONNECTED;
			}

			// If this is DCC chat, inform the caller about accept() 
			// success or failure.
			// Otherwise (DCC send) there is no reason.
			if ( dcc->dccmode == LIBIRC_DCC_CHAT )
			{
				libirc_mutex_unlock (&ircsession->mutex_dcc);
				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
				libirc_mutex_lock (&ircsession->mutex_dcc);
			}

			if ( err )
				libirc_dcc_destroy_nolock (ircsession, dcc->id);
		}

		if ( dcc->state == LIBIRC_STATE_CONNECTING
		&& FD_ISSET (dcc->sock, out_set) )
		{
			// Now we have to determine whether the socket is connected 
			// or the connect is failed
			struct sockaddr_in saddr;
			socklen_t slen = sizeof(saddr);
			int err = 0;

			if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 )
				err = LIBIRC_ERR_CONNECT;

			// On success, change the state
			if ( err == 0 )
				dcc->state = LIBIRC_STATE_CONNECTED;

			// If this is DCC chat, inform the caller about connect()
			// success or failure.
			// Otherwise (DCC send) there is no reason.
			if ( dcc->dccmode == LIBIRC_DCC_CHAT )
			{
				libirc_mutex_unlock (&ircsession->mutex_dcc);
				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
				libirc_mutex_lock (&ircsession->mutex_dcc);
			}

			if ( err )
				libirc_dcc_destroy_nolock (ircsession, dcc->id);
		}

		if ( dcc->state == LIBIRC_STATE_CONNECTED
		|| dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
		{
			if ( FD_ISSET (dcc->sock, in_set) )
			{
				int length, offset = 0, err = 0;
		
				unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset;

				length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount);

				if ( length < 0 )
				{
					err = LIBIRC_ERR_READ;
				}	
				else if ( length == 0 )
				{
					err = LIBIRC_ERR_CLOSED;

					if ( dcc->dccsend_file_fp )
					{
						fclose (dcc->dccsend_file_fp);
						dcc->dccsend_file_fp = 0;
					}
				}
				else
				{
					dcc->incoming_offset += length;

					if ( dcc->dccmode != LIBIRC_DCC_CHAT )
						offset = dcc->incoming_offset;
					else
						offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset);

					/*
					 * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any
                     * callbacks (except there is an error). We just receive
                     * the data, and compare it with the amount sent.
                     */
					if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
					{
						if ( dcc->dccmode != LIBIRC_DCC_SENDFILE )
							abort();

						if ( dcc->incoming_offset == 4 )
						{
							unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

							// Sent size confirmed
							if ( dcc->file_confirm_offset == received_size )
							{
								dcc->state = LIBIRC_STATE_CONNECTED;
								dcc->incoming_offset = 0;
							}
							else
								err = LIBIRC_ERR_WRITE;
						}
					}
					else
					{
						/*
						 * If it is DCC_CHAT, we send a 0-terminated string 
						 * (which is smaller than offset). Otherwise we send
	                     * a full buffer. 
	                     */
						libirc_mutex_unlock (&ircsession->mutex_dcc);

						if ( dcc->dccmode != LIBIRC_DCC_CHAT )
						{
							if ( dcc->dccmode != LIBIRC_DCC_RECVFILE )
								abort();

							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset);

                            /*
                             * If the session is not terminated in callback,
                             * put the sent amount into the sent_packet_size_net_byteorder
                             */
                             if ( dcc->state != LIBIRC_STATE_REMOVED )
                             {
                             	dcc->state = LIBIRC_STATE_CONFIRM_SIZE;
                             	dcc->file_confirm_offset += offset;
                             	*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
                             	dcc->outgoing_offset = 4;
							}
						}
						else
							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf));

						libirc_mutex_lock (&ircsession->mutex_dcc);

						if ( dcc->incoming_offset - offset > 0 )
							memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset);

						dcc->incoming_offset -= offset;
					}
				}

                /*
                 * If error arises somewhere above, we inform the caller 
                 * of failure, and destroy this session.
                 */
				if ( err )
				{
					libirc_mutex_unlock (&ircsession->mutex_dcc);
					(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
					libirc_mutex_lock (&ircsession->mutex_dcc);
					libirc_dcc_destroy_nolock (ircsession, dcc->id);
				}
			}

            /*
             * Session might be closed (with sock = -1) after the in_set 
             * processing, so before out_set processing we should check
             * for this case
			 */
			if ( dcc->state == LIBIRC_STATE_REMOVED )
				continue;

			/*
			 * Write bit set - we can send() something, and it won't block.
             */
			if ( FD_ISSET (dcc->sock, out_set) )
			{
				int length, offset, err = 0;

				/*
				 * Because in some cases outgoing_buf could be changed 
				 * asynchronously (by another thread), we should lock 
				 * it.
                 */
				libirc_mutex_lock (&dcc->mutex_outbuf);

				offset = dcc->outgoing_offset;
		
				if ( offset > 0 )
				{
					length = socket_send (&dcc->sock, dcc->outgoing_buf, offset);

					if ( length < 0 )
						err = LIBIRC_ERR_WRITE;
					else if ( length == 0 )
						err = LIBIRC_ERR_CLOSED;
					else
					{
						/*
						 * If this was DCC_SENDFILE, and we just sent a packet,
						 * change the state to wait for confirmation (and store
						 * sent packet size)
	                     */
						if ( dcc->state == LIBIRC_STATE_CONNECTED
						&& dcc->dccmode == LIBIRC_DCC_SENDFILE )
						{
							dcc->file_confirm_offset += offset;
							dcc->state = LIBIRC_STATE_CONFIRM_SIZE;

							libirc_mutex_unlock (&ircsession->mutex_dcc);
							libirc_mutex_unlock (&dcc->mutex_outbuf);
							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, offset);
							libirc_mutex_lock (&ircsession->mutex_dcc);
							libirc_mutex_lock (&dcc->mutex_outbuf);
						}

						if ( dcc->outgoing_offset - length > 0 )
							memmove (dcc->outgoing_buf, dcc->outgoing_buf + length, dcc->outgoing_offset - length);

						dcc->outgoing_offset -= length;

						/*
						 * If we just sent the confirmation data, change state 
						 * back.
                         */
						if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE
						&& dcc->dccmode == LIBIRC_DCC_RECVFILE
						&& dcc->outgoing_offset == 0 )
						{
							/*
							 * If the file is already received, we should inform
                             * the caller, and close the session.
                             */
							if ( dcc->received_file_size == dcc->file_confirm_offset )
                            {
								libirc_mutex_unlock (&ircsession->mutex_dcc);
								libirc_mutex_unlock (&dcc->mutex_outbuf);
								(*dcc->cb)(ircsession, dcc->id, 0, dcc->ctx, 0, 0);
								libirc_dcc_destroy_nolock (ircsession, dcc->id);
                            }
                            else
                            {
                            	/* Continue to receive the file */
								dcc->state = LIBIRC_STATE_CONNECTED;
							}
						}
					}
				}

				libirc_mutex_unlock (&dcc->mutex_outbuf);

                /*
                 * If error arises somewhere above, we inform the caller 
                 * of failure, and destroy this session.
                 */
				if ( err )
				{
					libirc_mutex_unlock (&ircsession->mutex_dcc);
					(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
					libirc_mutex_lock (&ircsession->mutex_dcc);

					libirc_dcc_destroy_nolock (ircsession, dcc->id);
				}
			}
		}
	}

	libirc_mutex_unlock (&ircsession->mutex_dcc);
}
Beispiel #2
0
static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set)
{
	irc_dcc_session_t * dcc;

	/*
	 * We need to use such a complex scheme here, because on every callback
     * a number of DCC sessions could be destroyed.
     */
	libirc_mutex_lock (&ircsession->mutex_dcc);

	for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next )
	{
		if ( dcc->state == LIBIRC_STATE_LISTENING
		&& FD_ISSET (dcc->sock, in_set) )
		{
			socklen_t len = sizeof(dcc->remote_addr);
			int nsock, err = 0;

			// New connection is available; accept it.
			if ( socket_accept (&dcc->sock, (socket_t*)&nsock, (struct sockaddr *) &dcc->remote_addr, &len) )
				err = LIBIRC_ERR_ACCEPT;

			// On success, change the active socket and change the state
			if ( err == 0 )
			{
				// close the listen socket, and replace it by a newly
				// accepted
				socket_close (&dcc->sock);
				dcc->sock = nsock;
				dcc->state = LIBIRC_STATE_CONNECTED;
			}

			// If this is DCC chat, inform the caller about accept()
			// success or failure.
			// Otherwise (DCC send) there is no reason.
			if ( dcc->dccmode == LIBIRC_DCC_CHAT )
			{
				libirc_mutex_unlock (&ircsession->mutex_dcc);
				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
				libirc_mutex_lock (&ircsession->mutex_dcc);
			}

			if ( err )
				libirc_dcc_destroy_nolock (ircsession, dcc->id);
		}

		if ( dcc->state == LIBIRC_STATE_CONNECTING
		&& FD_ISSET (dcc->sock, out_set) )
		{
			// Now we have to determine whether the socket is connected
			// or the connect is failed
			struct sockaddr_in saddr;
			socklen_t slen = sizeof(saddr);
			int err = 0;

			if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 )
				err = LIBIRC_ERR_CONNECT;

			// On success, change the state
			if ( err == 0 )
				dcc->state = LIBIRC_STATE_CONNECTED;

			// If this is DCC chat, inform the caller about connect()
			// success or failure.
			// Otherwise (DCC send) there is no reason.
			if ( dcc->dccmode == LIBIRC_DCC_CHAT )
			{
				libirc_mutex_unlock (&ircsession->mutex_dcc);
				(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
				libirc_mutex_lock (&ircsession->mutex_dcc);
			}

			if ( err )
				libirc_dcc_destroy_nolock (ircsession, dcc->id);
		}

		if ( dcc->state == LIBIRC_STATE_CONNECTED
		|| dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
		{
			if ( FD_ISSET (dcc->sock, in_set) )
			{
				int length, offset = 0, err = 0;

				unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset;

				length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount);

				if ( length < 0 )
				{
					err = LIBIRC_ERR_READ;
				}
				else if ( length == 0 )
				{
					err = LIBIRC_ERR_CLOSED;

					if ( dcc->dccsend_file_fp )
					{
						fclose (dcc->dccsend_file_fp);
						dcc->dccsend_file_fp = 0;
					}
				}
				else
				{
					dcc->incoming_offset += length;

					if ( dcc->dccmode != LIBIRC_DCC_CHAT )
						offset = dcc->incoming_offset;
					else
						offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset);

					/*
					 * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any
                     * callbacks (except there is an error). We just receive
                     * the data, and compare it with the amount sent.
                     */
					if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
					{
						if ( dcc->dccmode != LIBIRC_DCC_SENDFILE )
							abort();

						if ( dcc->incoming_offset == 4 )
						{
							// The order is big-endian
							const unsigned char * bptr = (const unsigned char *) dcc->incoming_buf;
							unsigned int received_size = (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8)  | bptr[3];

							// Sent size confirmed
							if ( dcc->file_confirm_offset == received_size )
							{
								dcc->state = LIBIRC_STATE_CONNECTED;
								dcc->incoming_offset = 0;
							}
							else
								err = LIBIRC_ERR_WRITE;
						}
					}
					else
					{
						/*
						 * If it is DCC_CHAT, we send a 0-terminated string
						 * (which is smaller than offset). Otherwise we send
	                     * a full buffer.
	                     */
						libirc_mutex_unlock (&ircsession->mutex_dcc);

						if ( dcc->dccmode != LIBIRC_DCC_CHAT )
						{
							if ( dcc->dccmode != LIBIRC_DCC_RECVFILE )
								abort();

							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset);

                            /*
                             * If the session is not terminated in callback,
                             * put the sent amount into the sent_packet_size_net_byteorder
                             */
                             if ( dcc->state != LIBIRC_STATE_REMOVED )
                             {
                             	dcc->state = LIBIRC_STATE_CONFIRM_SIZE;
                             	dcc->file_confirm_offset += offset;

								// Store as big endian
								dcc->outgoing_buf[0] = (char) dcc->file_confirm_offset >> 24;
								dcc->outgoing_buf[1] = (char) dcc->file_confirm_offset >> 16;
								dcc->outgoing_buf[2] = (char) dcc->file_confirm_offset >> 8;
								dcc->outgoing_buf[3] = (char) dcc->file_confirm_offset;
                             	dcc->outgoing_offset = 4;
							}
						}
						else
							(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf));

						libirc_mutex_lock (&ircsession->mutex_dcc);

						if ( dcc->incoming_offset - offset > 0 )
							memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset);

						dcc->incoming_offset -= offset;
					}
				}

                /*
                 * If error arises somewhere above, we inform the caller
                 * of failure, and destroy this session.
                 */
				if ( err )
				{
					libirc_mutex_unlock (&ircsession->mutex_dcc);
					(*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
					libirc_mutex_lock (&ircsession->mutex_dcc);
					libirc_dcc_destroy_nolock (ircsession, dcc->id);
				}
			}