注册注销登录退出jabber

 
注册Jabber用户
static BOOL CALLBACK JabberRegisterDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
       ThreadData *thread, *regInfo;
 
       switch ( msg ) {
       case WM_INITDIALOG:
       {
              TranslateDialogDefault( hwndDlg );
              regInfo = ( ThreadData* ) lParam;
              TCHAR text[256];
              mir_sntprintf( text, SIZEOF(text), STR_FORMAT, TranslateT( "Register" ), regInfo->username, regInfo->server, regInfo->port );
              SetDlgItemText( hwndDlg, IDC_REG_STATUS, text );
              SetWindowLong( hwndDlg, GWL_USERDATA, ( LONG )regInfo );
              return TRUE;
       }
       case WM_COMMAND:
              switch ( LOWORD( wParam )) {
              case IDOK:
                     ShowWindow( GetDlgItem( hwndDlg, IDOK ), SW_HIDE );
                     ShowWindow( GetDlgItem( hwndDlg, IDCANCEL ), SW_HIDE );
                     ShowWindow( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ), SW_SHOW );
                     ShowWindow( GetDlgItem( hwndDlg, IDCANCEL2 ), SW_SHOW );
                     regInfo = ( ThreadData* ) GetWindowLong( hwndDlg, GWL_USERDATA );
                     thread = new ThreadData( JABBER_SESSION_REGISTER );
                     _tcsncpy( thread->username, regInfo->username, SIZEOF( thread->username ));
                     strncpy( thread->password, regInfo->password, SIZEOF( thread->password ));
                     strncpy( thread->server, regInfo->server, SIZEOF( thread->server ));
                     strncpy( thread->manualHost, regInfo->manualHost, SIZEOF( thread->manualHost ));
                     thread->port = regInfo->port;
                     thread->useSSL = regInfo->useSSL;
                     thread->reg_hwndDlg = hwndDlg;
                     mir_forkthread(( pThreadFunc )JabberServerThread, thread );
                     return TRUE;
              case IDCANCEL:
              case IDOK2:
                     EndDialog( hwndDlg, 0 );
                     return TRUE;
              }
              break;
       case WM_JABBER_REGDLG_UPDATE:     // wParam=progress ( 0-100 ), lparam=status string
              if (( TCHAR* )lParam == NULL )
                     SetDlgItemText( hwndDlg, IDC_REG_STATUS, TranslateT( "No message" ));
              else
                     SetDlgItemText( hwndDlg, IDC_REG_STATUS, ( TCHAR* )lParam );
              if ( wParam >= 0 )
                     SendMessage( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ), PBM_SETPOS, wParam, 0 );
              if ( wParam >= 100 ) {
                     ShowWindow( GetDlgItem( hwndDlg, IDCANCEL2 ), SW_HIDE );
                     ShowWindow( GetDlgItem( hwndDlg, IDOK2 ), SW_SHOW );
              }
              else SetFocus( GetDlgItem( hwndDlg, IDC_PROGRESS_REG ));
              return TRUE;
       }
 
       return FALSE;
}
 
登录Jabber
JabberServerThread
都在线程JabberServerThread里, 在程序里加了注释, 仔细分析代码吧:
void __cdecl JabberServerThread( ThreadData* info )
{
       DBVARIANT dbv;
       char* buffer;
       int datalen;
       int oldStatus;
       PVOID ssl;
 
       JabberLog( "Thread started: type=%d", info->type );
 
       info->resolveID = -1;
       info->auth = NULL;
       if ( info->type == JABBER_SESSION_NORMAL )
    {
              // Normal server connection, we will fetch all connection parameters
              // e.g. username, password, etc. from the database.
 
              if ( jabberThreadInfo != NULL )
        {
                     // Will not start another connection thread if a thread is already running.
                     // Make APC call to the main thread. This will immediately wake the thread up
                     // in case it is asleep in the reconnect loop so that it will immediately
                     // reconnect.
                     QueueUserAPC( JabberDummyApcFunc, jabberThreadInfo->hThread, 0 );
                     JabberLog( "Thread ended, another normal thread is running" );
                     //zhangcong//mir_free( info );
            delete info; info = NULL; //zhangcong
                     return;
              }
 
              jabberThreadInfo = info;
              if ( streamId )
            mir_free( streamId );
              streamId = NULL;
 
        //读取登录帐号
              if ( !JGetStringT( NULL, "LoginName", &dbv )) {
                     _tcsncpy( info->username, dbv.ptszVal, SIZEOF( info->username )-1 );
                     JFreeVariant( &dbv );
              }
              else {
                     JabberLog( "Thread ended, login name is not configured" );
                     JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
LBL_FatalError:
                     jabberThreadInfo = NULL;
                     oldStatus = jabberStatus;
                     jabberStatus = ID_STATUS_OFFLINE;
                     JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
LBL_Exit:
                     //zhangcong//mir_free( info );
            delete info; info = NULL; //zhangcong
                     return;
              }
 
              if ( *rtrim(info->username) == '/0' ) {
                     JabberLog( "Thread ended, login name is not configured" );
                     JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
                     goto LBL_FatalError;
              }
 
        //读取jabber server地址
              if ( !DBGetContactSetting( NULL, jabberProtoName, "LoginServer", &dbv )) {
                     strncpy( info->server, dbv.pszVal, SIZEOF( info->server )-1 );
                     JFreeVariant( &dbv );
              }
              else {
                     JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
                     JabberLog( "Thread ended, login server is not configured" );
                     goto LBL_FatalError;
              }
 
        //读取资源 (在注册用户时指定的)
              if ( !JGetStringT( NULL, "Resource", &dbv )) {
                     _tcsncpy( info->resource, dbv.ptszVal, SIZEOF( info->resource )-1 );
                     JFreeVariant( &dbv );
              }
              else
        {
            _tcscpy( info->resource, _T("Miranda"));
        }
 
        //构造出完整的Jabber ID
              TCHAR jidStr[128];
              mir_sntprintf( jidStr, SIZEOF( jidStr ), _T("%s@") _T(TCHAR_STR_PARAM) _T("/%s"), info->username, info->server, info->resource );
              _tcsncpy( info->fullJID, jidStr, SIZEOF( info->fullJID )-1 );
 
        //读取登录密码, 如果已经保存的话
              if ( JGetByte( "SavePassword", TRUE ) == FALSE )
        {
                     mir_sntprintf( jidStr, SIZEOF( jidStr ), _T("%s@") _T(TCHAR_STR_PARAM), info->username, info->server );
 
                     // Ugly hack: continue logging on only the return value is &( onlinePassword[0] )
                     // because if WM_QUIT while dialog box is still visible, p is returned with some
                     // exit code which may not be NULL.
                     // Should be better with modeless.
                     onlinePassword[0] = ( char )-1;
                     hEventPasswdDlg = CreateEvent( NULL, FALSE, FALSE, NULL );
                     QueueUserAPC( JabberPasswordCreateDialogApcProc, hMainThread, ( DWORD )jidStr );
                     WaitForSingleObject( hEventPasswdDlg, INFINITE );
                     CloseHandle( hEventPasswdDlg );
 
                     if ( onlinePassword[0] == ( TCHAR ) -1 ) {
                            JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
                            JabberLog( "Thread ended, password request dialog was canceled" );
                            goto LBL_FatalError;
                     }
                     strncpy( info->password, onlinePassword, SIZEOF( info->password ));
                     info->password[ SIZEOF( info->password )-1] = '/0';
              }
              else {
                     if ( DBGetContactSetting( NULL, jabberProtoName, "Password", &dbv )) {
                            JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID );
                            JabberLog( "Thread ended, password is not configured" );
                            goto LBL_FatalError;
                     }
                     JCallService( MS_DB_CRYPT_DECODESTRING, strlen( dbv.pszVal )+1, ( LPARAM )dbv.pszVal );
                     strncpy( info->password, dbv.pszVal, SIZEOF( info->password ));
                     info->password[SIZEOF( info->password )-1] = '/0';
                     JFreeVariant( &dbv );
              }
 
        //读取jabber server端口, 如果指定了Jabber server地址的话, 读取指定的地址
              if ( JGetByte( "ManualConnect", FALSE ) == TRUE ) {
                     if ( !DBGetContactSetting( NULL, jabberProtoName, "ManualHost", &dbv )) {
                            strncpy( info->manualHost, dbv.pszVal, SIZEOF( info->manualHost ));
                            info->manualHost[sizeof( info->manualHost )-1] = '/0';
                            JFreeVariant( &dbv );
                     }
                     info->port = JGetWord( NULL, "ManualPort", JABBER_DEFAULT_PORT );
              }
              else
        {
            info->port = JGetWord( NULL, "Port", JABBER_DEFAULT_PORT );
        }
 
        //是否使用SSL
              info->useSSL = JGetByte( "UseSSL", FALSE );
       }
 
       else if ( info->type == JABBER_SESSION_REGISTER ) //注册新用户连接
    {
              // Register new user connection, all connection parameters are already filled-in.
              // Multiple thread allowed, although not possible : )
              // thinking again.. multiple thread should not be allowed
              info->reg_done = FALSE;
              SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 25, ( LPARAM )TranslateT( "Connecting..." ));
              iqIdRegGetReg = -1;
              iqIdRegSetReg = -1;
       }
       else
    {
              JabberLog( "Thread ended, invalid session type" );
              goto LBL_Exit;
       }
 
       char connectHost[128];
       if ( info->manualHost[0] == 0 ) {
              int port_temp;
              strncpy( connectHost, info->server, SIZEOF(info->server));
              if ( port_temp = xmpp_client_query( connectHost )) { // port_temp will be > 0 if resolution is successful
                     JabberLog("%s%s resolved to %s:%d","_xmpp-client._tcp.",info->server,connectHost,port_temp);
                     if (info->port==0 || info->port==5222)
                            info->port = port_temp;
              }
              else JabberLog("%s%s not resolved", "_xmpp-client._tcp.", connectHost);
       }
       else
    {
        strncpy( connectHost, info->manualHost, SIZEOF(connectHost)); // do not resolve if manual host is selected
    }
 
       JabberLog( "Thread type=%d server='%s' port='%d'", info->type, connectHost, info->port );
 
       int jabberNetworkBufferSize = 2048;
       if (( buffer=( char* )mir_alloc( jabberNetworkBufferSize+1 )) == NULL ) {     // +1 is for '/0' when debug logging this buffer
              JabberLog( "Cannot allocate network buffer, thread ended" );
              if ( info->type == JABBER_SESSION_NORMAL ) {
                     oldStatus = jabberStatus;
                     jabberStatus = ID_STATUS_OFFLINE;
                     JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
                     JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
                     jabberThreadInfo = NULL;
              }
              else if ( info->type == JABBER_SESSION_REGISTER ) {
                     SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Not enough memory" ));
              }
              JabberLog( "Thread ended, network buffer cannot be allocated" );
              goto LBL_Exit;
       }
 
    //与Jabber server建立socket连接
       info->s = JabberWsConnect( connectHost, info->port );
       if ( info->s == NULL )
    {
              JabberLog( "Connection failed ( %d )", WSAGetLastError());
              if ( info->type == JABBER_SESSION_NORMAL )
        {
                     if ( jabberThreadInfo == info )
            {
                            oldStatus = jabberStatus;
                            jabberStatus = ID_STATUS_OFFLINE;
                            JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
                            JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
                            jabberThreadInfo = NULL;
            }    
        }
              else if ( info->type == JABBER_SESSION_REGISTER )
        {
                     SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Cannot connect to the server" ));
        }
 
              JabberLog( "Thread ended, connection failed" );
              mir_free( buffer );
              goto LBL_Exit;
       }
 
       // Determine local IP
       int socket = JCallService( MS_NETLIB_GETSOCKET, ( WPARAM ) info->s, 0 );
       if ( info->type==JABBER_SESSION_NORMAL && socket!=INVALID_SOCKET ) {
              struct sockaddr_in saddr;
              int len;
 
              len = sizeof( saddr );
              getsockname( socket, ( struct sockaddr * ) &saddr, &len );
              jabberLocalIP = saddr.sin_addr.S_un.S_addr;
              JabberLog( "Local IP = %s", inet_ntoa( saddr.sin_addr ));
       }
 
       if ( info->useSSL )
    {
              JabberLog( "Intializing SSL connection" );
              if ( JabberSslInit() && socket != INVALID_SOCKET )
        {
                     JabberLog( "SSL using socket = %d", socket );
                     if (( ssl=pfn_SSL_new( jabberSslCtx )) != NULL )
            {
                            JabberLog( "SSL create context ok" );
                            if ( pfn_SSL_set_fd( ssl, socket ) > 0 )
                {
                                   JabberLog( "SSL set fd ok" );
                                   if ( pfn_SSL_connect( ssl ) > 0 )
                    {
                                          JabberLog( "SSL negotiation ok" );
                                          info->ssl = ssl;       // This make all communication on this handle use SSL
                                          JabberLog( "SSL enabled for handle = %d", info->s );
                                   }
                                   else
                    {
                                          JabberLog( "SSL negotiation failed" );
                                          pfn_SSL_free( ssl );
                    }    
                }
                            else
                {
                                   JabberLog( "SSL set fd failed" );
                                   pfn_SSL_free( ssl );
                }    
            }    
        }
 
              if ( !info->ssl )
        {
                     if ( info->type == JABBER_SESSION_NORMAL )
            {
                            oldStatus = jabberStatus;
                            jabberStatus = ID_STATUS_OFFLINE;
                            JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
                            JSendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK );
                            if ( jabberThreadInfo == info )
                                   jabberThreadInfo = NULL;
                     }
                     else if ( info->type == JABBER_SESSION_REGISTER )
            {
                            SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Cannot connect to the server" ));
                     }
                     mir_free( buffer );
                     if ( !hLibSSL )
                            MessageBox( NULL, TranslateT( "The connection requires an OpenSSL library, which is not installed." ), TranslateT( "Jabber Connection Error" ), MB_OK|MB_ICONSTOP|MB_SETFOREGROUND );
                     JabberLog( "Thread ended, SSL connection failed" );
                     goto LBL_Exit;
        }    
    }
 
       // User may change status to OFFLINE while we are connecting above
       if ( jabberDesiredStatus != ID_STATUS_OFFLINE || info->type == JABBER_SESSION_REGISTER )
    {
 
              if ( info->type == JABBER_SESSION_NORMAL )
        {
                     jabberConnected = TRUE;
                     int len = _tcslen( info->username ) + strlen( info->server )+1;
                     jabberJID = ( TCHAR* )mir_alloc( sizeof( TCHAR)*( len+1 ));
                     mir_sntprintf( jabberJID, len+1, _T("%s@") _T(TCHAR_STR_PARAM), info->username, info->server );
                     if ( JGetByte( "KeepAlive", 1 ))
                            jabberSendKeepAlive = TRUE;
                     else
                            jabberSendKeepAlive = FALSE;
            //连接建立以后创建一个保持活跃的线程
                     mir_forkthread(( pThreadFunc )JabberKeepAliveThread, info );
              }
 
              xmlStreamInitializeNow( info );
 
              JabberLog( "Entering main recv loop" );
              datalen = 0;
 
        //zhangcong//main recv loop, 出现异常时,将break loop
              for ( ;; )
        {
                     int recvResult = info->recv( buffer+datalen, jabberNetworkBufferSize-datalen);
            int bytesParsed;
                     JabberLog( "recvResult = %d", recvResult );
                     if ( recvResult <= 0 )
                            break;
                     datalen += recvResult;
 
                     buffer[datalen] = '/0';
                     if ( info->ssl && DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", TRUE ) == TRUE )
            {
                            // Emulate netlib log feature for SSL connection
                            char* szLogBuffer = ( char* )mir_alloc( recvResult+128 );
                            if ( szLogBuffer != NULL )
                {
                                   strcpy( szLogBuffer, "( SSL ) Data received/n" );
                                   memcpy( szLogBuffer+strlen( szLogBuffer ), buffer+datalen-recvResult, recvResult+1 /* also copy /0 */ );
                                   Netlib_Logf( hNetlibUser, "%s", szLogBuffer );   // %s to protect against when fmt tokens are in szLogBuffer causing crash
                                   mir_free( szLogBuffer );
                }    
            }
 
            //XmlParse接收到的数据
                     bytesParsed = JabberXmlParse( &xmlState, buffer );
                     JabberLog( "bytesParsed = %d", bytesParsed );
                     if ( bytesParsed > 0 )
            {
                            if ( bytesParsed < datalen )
                                   memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
                            datalen -= bytesParsed;
                     }
                     else if ( datalen == jabberNetworkBufferSize )
            {
                //如果缓冲区已满, 扩充缓冲区
                            jabberNetworkBufferSize += 65536;
                            JabberLog( "Increasing network buffer size to %d", jabberNetworkBufferSize );
                            if (( buffer=( char* )mir_realloc( buffer, jabberNetworkBufferSize+1 )) == NULL ) {
                                   JabberLog( "Cannot reallocate more network buffer, go offline now" );
                                   break;
                }    
            }
                     else
            {
                JabberLog( "Unknown state: bytesParsed=%d, datalen=%d, jabberNetworkBufferSize=%d", bytesParsed, datalen, jabberNetworkBufferSize );
            }
 
                     if (xmlStreamToBeInitialized)
                xmlStreamInitializeNow(info);
              }
 
        //出现异常后, 跳出main recv loop, 清理现场,设置状态
 
              JabberXmlDestroyState(&xmlState);
 
              if ( info->type == JABBER_SESSION_NORMAL )
        {
                     jabberOnline = FALSE;
                     jabberConnected = FALSE;
                     JabberEnableMenuItems( FALSE );
                     if ( hwndJabberChangePassword )
            {
                            //DestroyWindow( hwndJabberChangePassword );
                            // Since this is a different thread, simulate the click on the cancel button instead
                            SendMessage( hwndJabberChangePassword, WM_COMMAND, MAKEWORD( IDCANCEL, 0 ), 0 );
                     }
 
                     if ( jabberChatDllPresent )
                            QueueUserAPC( JabberOfflineChatWindows, hMainThread, 0 );
 
                     JabberListRemoveList( LIST_CHATROOM );
                     if ( hwndJabberAgents )
                            SendMessage( hwndJabberAgents, WM_JABBER_CHECK_ONLINE, 0, 0 );
                     if ( hwndJabberGroupchat )
                            SendMessage( hwndJabberGroupchat, WM_JABBER_CHECK_ONLINE, 0, 0 );
                     if ( hwndJabberJoinGroupchat )
                            SendMessage( hwndJabberJoinGroupchat, WM_JABBER_CHECK_ONLINE, 0, 0 );
 
                     // Set status to offline
                     oldStatus = jabberStatus;
                     jabberStatus = ID_STATUS_OFFLINE;
                     JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
 
                     // Set all contacts to offline
                     HANDLE hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDFIRST, 0, 0 );
                     while ( hContact != NULL )
            {
                            if ( !lstrcmpA(( char* )JCallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM ) hContact, 0 ), jabberProtoName ))
                                   if ( JGetWord( hContact, "Status", ID_STATUS_OFFLINE ) != ID_STATUS_OFFLINE )
                                          JSetWord( hContact, "Status", ID_STATUS_OFFLINE );
 
                            hContact = ( HANDLE ) JCallService( MS_DB_CONTACT_FINDNEXT, ( WPARAM ) hContact, 0 );
                     }
 
                     mir_free( jabberJID );
                     jabberJID = NULL;
                     jabberLoggedInTime = 0;
                     JabberListWipe();
                     if ( hwndJabberAgents )
            {
                            SendMessage( hwndJabberAgents, WM_JABBER_AGENT_REFRESH, 0, ( LPARAM )"" );
                            SendMessage( hwndJabberAgents, WM_JABBER_TRANSPORT_REFRESH, 0, 0 );
                     }
                     if ( hwndJabberVcard )
                            SendMessage( hwndJabberVcard, WM_JABBER_CHECK_ONLINE, 0, 0 );
              }
              else if ( info->type==JABBER_SESSION_REGISTER && !info->reg_done )
        {
                     SendMessage( info->reg_hwndDlg, WM_JABBER_REGDLG_UPDATE, 100, ( LPARAM )TranslateT( "Error: Connection lost" ));
        }
       }
       else if ( info->type == JABBER_SESSION_NORMAL )
    {
              oldStatus = jabberStatus;
              jabberStatus = ID_STATUS_OFFLINE;
              JSendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, ( HANDLE ) oldStatus, jabberStatus );
       }
 
       JabberLog( "Thread ended: type=%d server='%s'", info->type, info->server );
 
       if ( info->type==JABBER_SESSION_NORMAL && jabberThreadInfo==info )
    {
              if ( streamId ) mir_free( streamId );
              streamId = NULL;
              jabberThreadInfo = NULL;
       }
 
       mir_free( buffer );
       JabberLog( "Exiting ServerThread" );
       goto LBL_Exit;
}
 
JabberRecvMessage
int JabberRecvMessage( WPARAM wParam, LPARAM lParam )
{
       CCSDATA *ccs = ( CCSDATA* )lParam;
       PROTORECVEVENT *pre = ( PROTORECVEVENT* )ccs->lParam;
 
       DBEVENTINFO dbei = { 0 };
       dbei.cbSize = sizeof( dbei );
       dbei.szModule = jabberProtoName;
       dbei.timestamp = pre->timestamp;
       dbei.flags = pre->flags&PREF_CREATEREAD?DBEF_READ:0;
       dbei.eventType = EVENTTYPE_MESSAGE;
       dbei.cbBlob = strlen( pre->szMessage ) + 1;
       if ( pre->flags & PREF_UNICODE )
              dbei.cbBlob *= ( sizeof( wchar_t )+1 );
 
       dbei.pBlob = ( PBYTE ) pre->szMessage;
       JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei );
       return 0;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值