Develop and Download Open Source Software

Browse CVS Repository

Diff of /xoonips/AL/commonal.cc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.36 by aga, Thu Jan 13 04:19:22 2005 UTC revision 1.37 by aga, Fri Jan 14 10:36:59 2005 UTC
# Line 2  Line 2 
2   *   *
3   * $Revision$   * $Revision$
4   * $Log$   * $Log$
5     * Revision 1.37  2005/01/14 10:36:59  aga
6     * ・index関係の処理を追加.
7     * ・insertAccountでprivate indexを作成するよう修正.
8     * ・insertGroupでgroup index を作成するよう修正.
9     *
10   * Revision 1.36  2005/01/13 04:19:22  aga   * Revision 1.36  2005/01/13 04:19:22  aga
11   * ・VPをXNPに変換.   * ・VPをXNPに変換.
12   *   *
# Line 171  using namespace std; Line 176  using namespace std;
176  #include "session.h"  #include "session.h"
177  #include "criteria.h"  #include "criteria.h"
178  #include "commonal.h"  #include "commonal.h"
179    #include "item.h"
180    #include "index.h"
181    
182  static string dbprefix; //!< XOOPSデータベーステーブルのPREFIX  static string dbprefix; //!< XOOPSデータベーステーブルのPREFIX
183    
# Line 178  static SQLHANDLE henv = NULL; Line 185  static SQLHANDLE henv = NULL;
185  static SQLHANDLE hdbc = NULL;  static SQLHANDLE hdbc = NULL;
186  static SQLHANDLE hstmt = NULL;  static SQLHANDLE hstmt = NULL;
187    
188    static result_t insertIndexInternal( sessionid_t sid, index_t *index, indexid_t *xid );
189    
190  static string odbcDiagString( SQLSMALLINT HandleType, SQLHANDLE hstmt, SQLRETURN sqlcode );  static string odbcDiagString( SQLSMALLINT HandleType, SQLHANDLE hstmt, SQLRETURN sqlcode );
191  static result_t deleteMemberNoLimit( sessionid_t sid, groupid_t gid, userid_t uid );  static result_t deleteMemberNoLimit( sessionid_t sid, groupid_t gid, userid_t uid );
192    
# Line 204  static result_t countResultRows( const c Line 213  static result_t countResultRows( const c
213      return ret;      return ret;
214  }  }
215    
216    /** SQLを実行する。結果は捨てる。
217      * @param sql sql
218      * @return result_t
219      */
220    static result_t querySimple( const char *functionName, string &sql ){
221        result_t ret = RES_ERROR;
222        SQLRETURN sqlcode;
223        SQLHANDLE hstmt = NULL;
224        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
225            if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), strlen( sql.c_str() ) ) ) == SQL_SUCCESS ){
226                ret = RES_OK;
227            }else{
228                string s( "SQLExecDirect in querySimple " );
229                s += functionName;
230                s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
231                s += "sql=";
232                s += sql;
233                setLastErrorString( s.c_str( ) );
234                ret = RES_DB_QUERY_ERROR;
235            }
236            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
237        }
238        else {
239            setLastErrorString( "SQLAllocHandle(SQL_HANDLE_STMT,...) in querySimple " );
240            ret = RES_ERROR;
241        }
242        return ret;
243    }
244    
245    /** SQLを実行し、1行目の最初の整数値(NULLなら0とみなす)のみ受け取る。
246      * @param sql sql
247      * @param u   整数値を受け取る変数。
248      * @return result_t
249      */
250    static result_t queryGetUnsignedInt( const char *functionName, string &sql, unsigned int *u ){
251        result_t ret = RES_ERROR;
252        SQLRETURN sqlcode;
253        SQLHANDLE hstmt = NULL;
254        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
255            if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), strlen( sql.c_str() ) ) ) == SQL_SUCCESS ){
256                SQLUINTEGER sInt = 0;
257                SQLINTEGER len;
258                SQLBindCol( hstmt, 1, SQL_C_ULONG, &sInt, 0, &len );
259                if( ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){
260                    if ( len == SQL_NULL_DATA )
261                        sInt = 0;
262                    *u = sInt;
263                    ret = RES_OK;
264                }else{
265                    string s( "SQLFetch in queryGetUnsignedInt " );
266                    s += functionName;
267                    setLastErrorString( s.c_str( ) );
268                    ret = RES_ERROR;
269                }
270            }else{
271                string s( "SQLExecDirect in queryGetUnsignedInt " );
272                s += functionName;
273                s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
274                s += "sql=";
275                s += sql;
276                setLastErrorString( s.c_str( ) );
277                ret = RES_DB_QUERY_ERROR;
278            }
279            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
280        }
281        else {
282            setLastErrorString( "SQLAllocHandle(SQL_HANDLE_STMT,...) in queryGetUnsignedInt " );
283            ret = RES_ERROR;
284        }
285        return ret;
286    }
287    
288    /** sidからuidを得る。
289      * @param sid session id
290      * @param uid uidを受け取る変数
291      * @return
292      */
293    static result_t sessionID2UID( sessionid_t sid, userid_t *uid ){
294        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
295        
296        SQLRETURN sqlcode;
297        SQLHANDLE hstmt = NULL;    
298        
299        string sql = "SELECT uid FROM " + dbprefix + "_xnpaccount_session WHERE sid=" + unsignedIntToString(sid);
300        return queryGetUnsignedInt( "sessionID2UID", sql, (unsigned int*)uid );
301    }
302    
303  /**  /**
304   *   *
305   * 文字列コピー.   * 文字列コピー.
# Line 826  result_t getAccounts( sessionid_t sid, c Line 922  result_t getAccounts( sessionid_t sid, c
922      string sql;      string sql;
923      account_t* dst = new account_t[ uidsLen ];      account_t* dst = new account_t[ uidsLen ];
924            
925      sql += "SELECT u1.uid, u1.name, u1.uname, u1.email, u1.url, u1.user_avatar, u1.user_regdate, u1.user_icq, u1.user_from, u1.user_sig, u1.user_viewemail, u1.actkey, u1.user_aim, u1.user_yim, u1.user_msnm, u1.pass, u1.posts, u1.attachsig, u1.rank, u1.level, u1.theme, u1.timezone_offset, u1.last_login, u1.umode, u1.uorder, u1.notify_method, u1.notify_mode, u1.user_occ, u1.bio, u1.user_intrest, u1.user_mailok, u2.activate, u2.address, u2.division, u2.tel, u2.company_name, u2.country, u2.zipcode, u2.fax, u2.base_url, u2.notice_mail, u2.notice_mail_since ";      sql += "SELECT u1.uid, u1.name, u1.uname, u1.email, u1.url, u1.user_avatar, u1.user_regdate, u1.user_icq, u1.user_from, u1.user_sig, u1.user_viewemail, u1.actkey, u1.user_aim, u1.user_yim, u1.user_msnm, u1.pass, u1.posts, u1.attachsig, u1.rank, u1.level, u1.theme, u1.timezone_offset, u1.last_login, u1.umode, u1.uorder, u1.notify_method, u1.notify_mode, u1.user_occ, u1.bio, u1.user_intrest, u1.user_mailok, u2.activate, u2.address, u2.division, u2.tel, u2.company_name, u2.country, u2.zipcode, u2.fax, u2.base_url, u2.notice_mail, u2.notice_mail_since, u2.private_index_id ";
926      sql += "FROM " + dbprefix + "_users AS u1, " + dbprefix + "_xnpaccount_users AS u2 ";      sql += "FROM " + dbprefix + "_users AS u1, " + dbprefix + "_xnpaccount_users AS u2 ";
927      sql += "WHERE u1.uid = u2.uid ";      sql += "WHERE u1.uid = u2.uid ";
928      if( uidsLen > 0 ){      if( uidsLen > 0 ){
# Line 896  result_t getAccounts( sessionid_t sid, c Line 992  result_t getAccounts( sessionid_t sid, c
992                  dst[ i ].setBaseURL( getResultCol( hstmt, 40 ).c_str() );                  dst[ i ].setBaseURL( getResultCol( hstmt, 40 ).c_str() );
993                  dst[ i ].setNoticeMail( atoi( getResultCol( hstmt, 41 ).c_str() ));                  dst[ i ].setNoticeMail( atoi( getResultCol( hstmt, 41 ).c_str() ));
994                  dst[ i ].setNoticeMailSince( atoi( getResultCol( hstmt, 42 ).c_str() ));                  dst[ i ].setNoticeMailSince( atoi( getResultCol( hstmt, 42 ).c_str() ));
995                    dst[ i ].setPrivateIndexID( atoi( getResultCol( hstmt, 43 ).c_str() ));
996  #ifdef USE_SYSLOG  #ifdef USE_SYSLOG
997                  syslog( LOG_DEBUG, "set to account_t %d", i );                  syslog( LOG_DEBUG, "set to account_t %d", i );
998  #endif  #endif
# Line 963  result_t insertAccount( sessionid_t sid, Line 1060  result_t insertAccount( sessionid_t sid,
1060        1. insert user profile into xoops_users        1. insert user profile into xoops_users
1061        2. insert platform user profile into xnpaccount_users        2. insert platform user profile into xnpaccount_users
1062        3. add user to default platform group        3. add user to default platform group
1063          4. create private index
1064          5. update account set private_index_id=...
1065       */       */
1066            
1067      //1.xoopsのユーザテーブルに書き込む      //1.xoopsのユーザテーブルに書き込む
# Line 1189  result_t insertAccount( sessionid_t sid, Line 1288  result_t insertAccount( sessionid_t sid,
1288              ret = RES_ERROR;              ret = RES_ERROR;
1289          }          }
1290      }      }
1291        
1292        if ( ret == RES_OK ){
1293            //4.private indexを作成
1294            
1295            // private index用のsort_number生成
1296            string sql = "select min(sort_number) from " +
1297              dbprefix + "_xnpaccount_index where parent_index_id=" + unsignedIntToString(item::IID_ROOT) +
1298              " and open_level=" + unsignedIntToString(index::OL_PRIVATE);
1299            unsigned int sortNumber;
1300            ret = queryGetUnsignedInt( "insertAccount", sql, &sortNumber );
1301            sortNumber--;
1302            if ( ret == RES_OK ){
1303                // private index作成
1304                index_t index;
1305                index.setItemTypeID(item::ITID_INDEX);
1306                index.setContributorUID(*uid);
1307                index.setParentIndexID(item::IID_ROOT);
1308                index.setOwnerUID(*uid);
1309                index.setOpenLevel(index::OL_PRIVATE);
1310                index.setSortNumber(sortNumber);
1311                index.setTitle(account->getUname());
1312                indexid_t privateXID;
1313                ret = insertIndexInternal( sid, &index, &privateXID );
1314                if ( ret == RES_OK ){
1315                    // xnpaccuont_usersのprivate_index_idの書き換え
1316                    sql = "UPDATE " + dbprefix + "_xnpaccount_users SET private_index_id="
1317                      + unsignedIntToString(privateXID) + " WHERE uid=" + unsignedIntToString(*uid);
1318                    ret = querySimple( "insertAccount", sql );
1319                }
1320            }
1321        }
1322        
1323      return ret;      return ret;
1324  }  }
1325    
# Line 2181  result_t insertGroup( sessionid_t sid, c Line 2312  result_t insertGroup( sessionid_t sid, c
2312          setLastErrorString( "SQLAllocHandle(SQL_HANDLE_STMT,...) in insertGroup" );          setLastErrorString( "SQLAllocHandle(SQL_HANDLE_STMT,...) in insertGroup" );
2313          ret = RES_ERROR;          ret = RES_ERROR;
2314      }      }
2315        
2316        //
2317        if ( ret == RES_OK ){
2318            //group indexを作成
2319            
2320            //group index用のsort_num生成
2321            string sql = "SELECT MAX(sort_number) FROM " +
2322              dbprefix + "_xnpaccount_index WHERE parent_index_id=" + unsignedIntToString(item::IID_ROOT) +
2323              " AND (open_level=" + unsignedIntToString(index::OL_GROUP_ONLY) +
2324                " OR open_level=" + unsignedIntToString(index::OL_PUBLIC) + ")";
2325            unsigned int sortNumber;
2326            ret = queryGetUnsignedInt( "insertGroup", sql, &sortNumber );
2327            sortNumber++;
2328            if ( ret == RES_OK ){
2329                // group index作成
2330                index_t index;
2331                userid_t uid;
2332                ret = sessionID2UID( sid, &uid );
2333                if ( ret == RES_OK ){
2334                    index.setItemTypeID(item::ITID_INDEX);
2335                    index.setContributorUID(uid);
2336                    index.setParentIndexID(item::IID_ROOT);
2337                    index.setOwnerGID(*gid);
2338                    index.setOpenLevel(index::OL_GROUP_ONLY);
2339                    index.setSortNumber(sortNumber);
2340                    index.setTitle(group->getGname());
2341                    indexid_t groupXID;
2342                    ret = insertIndexInternal( sid, &index, &groupXID );
2343                    if ( ret == RES_OK ){
2344                        // xnpaccuont_groupsのgroup_index_idの書き換え
2345                        sql = "UPDATE " + dbprefix + "_xnpaccount_groups SET group_index_id="
2346                          + unsignedIntToString(groupXID) + " WHERE gid=" + unsignedIntToString(*gid);
2347                        ret = querySimple( "insertGroup", sql );
2348                    }
2349                }
2350            }
2351        }
2352        
2353      return ret;      return ret;
2354  }  }
2355    
# Line 2301  result_t getGroups( sessionid_t sid, gro Line 2470  result_t getGroups( sessionid_t sid, gro
2470      string sql;      string sql;
2471      group_t* dst = new group_t[ gidsLen ];      group_t* dst = new group_t[ gidsLen ];
2472            
2473      sql += "SELECT gid, gname, gdesc ";      sql += "SELECT gid, gname, gdesc, group_index_id ";
2474      sql += "FROM " + dbprefix + "_xnpaccount_groups ";      sql += "FROM " + dbprefix + "_xnpaccount_groups ";
2475      if( gidsLen > 0 ){      if( gidsLen > 0 ){
2476          sql += "WHERE gid=" + string( unsignedIntToString( gids[ 0 ] ) );          sql += "WHERE gid=" + string( unsignedIntToString( gids[ 0 ] ) );
# Line 2321  result_t getGroups( sessionid_t sid, gro Line 2490  result_t getGroups( sessionid_t sid, gro
2490                  dst[ i ].setGID( gid );                  dst[ i ].setGID( gid );
2491                  dst[ i ].setGname( getResultCol( hstmt, 2 ).c_str() );                  dst[ i ].setGname( getResultCol( hstmt, 2 ).c_str() );
2492                  dst[ i ].setDesc( getResultCol( hstmt, 3 ).c_str() );                  dst[ i ].setDesc( getResultCol( hstmt, 3 ).c_str() );
2493                    dst[ i ].setGroupIndexID( atoi(getResultCol( hstmt, 4 ).c_str()) );
2494                  ( *groupsLen )++;                  ( *groupsLen )++;
2495              }              }
2496              *groups = dst;              *groups = dst;
# Line 2471  result_t getUid( const char* uname, user Line 2641  result_t getUid( const char* uname, user
2641   */   */
2642  static result_t addSession( userid_t uid, sessionid_t* session )  static result_t addSession( userid_t uid, sessionid_t* session )
2643  {  {
2644      result_t ret = RES_DB_QUERY_ERROR;      char *functionName = "addSession";
     SQLRETURN sqlcode;  
2645      string sql = "INSERT INTO " + dbprefix + "_xnpaccount_session (uid) values (" +      string sql = "INSERT INTO " + dbprefix + "_xnpaccount_session (uid) values (" +
2646        unsignedIntToString(uid) + ")";        unsignedIntToString(uid) + ")";
2647      if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {      result_t ret = querySimple( functionName, sql );
2648          if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){      if ( ret == RES_OK ){
2649              SQLFreeHandle( SQL_HANDLE_STMT, hstmt );          sql = "SELECT LAST_INSERT_ID()";
2650              if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {          ret = queryGetUnsignedInt( functionName, sql, (unsigned int*)session );
                 sql = "SELECT LAST_INSERT_ID()";  
                 if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){  
                     sessionid_t sid;  
                     SQLINTEGER len;  
                     SQLBindCol( hstmt, 1, SQL_C_ULONG, &sid, 0, &len );  
                     if( ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){  
                         *session = sid;  
                         ret = RES_OK;  
                     }  
                 }  
                 SQLFreeHandle( SQL_HANDLE_STMT, hstmt );  
             }  
         }else{  
             SQLFreeHandle( SQL_HANDLE_STMT, hstmt );  
         }  
2651      }      }
2652      return ret;      return ret;
2653  }  }
# Line 2777  bool isValidSessionID( sessionid_t sid ) Line 2931  bool isValidSessionID( sessionid_t sid )
2931      return ret;      return ret;
2932  }  }
2933    
2934    
2935    
2936    // todo
2937    // basic informationに書き込み
2938    result_t insertItemInternal( sessionid_t sid, item_t *item, itemid_t *iid ){
2939        string sql = "INSERT INTO " + dbprefix + "_xnpaccount_item_basic "
2940          " ( item_type_id, uid, title, last_update_date, creation_date ) values ( "
2941         + unsignedIntToString(item->getItemTypeID()) + "," + unsignedIntToString(item->getContributorUID()) + ","
2942         + " '" + addSlashes(item->getTitle()) + "'," + unsignedIntToString(item->getLastUpdateDate())
2943         + "," + unsignedIntToString(item->getCreationDate()) + ")";
2944        result_t result = querySimple( "insertItemInternal", sql );
2945        if( result == RES_OK ){
2946            sql = "SELECT LAST_INSERT_ID()";
2947            result = queryGetUnsignedInt( "insertItemInternal", sql, (unsigned int*)iid );
2948        }
2949        return result;
2950    }
2951    
2952    
2953    /**
2954     *
2955     * 条件に一致するインデックスの一覧を得る。
2956     *
2957     * @param cond SQLの条件式。省略時は0。 条件式内でtx(index), ti(item), tlink(group_user_link) が利用可能。
2958     * @param uid  このuidが読み込み権限を持つようなインデックスのみ取得する。
2959                   ただしuid==0なら、読み込み権限を無視する。
2960     * @param indexes インデックスの一覧を返す変数
2961     * @param indexesLen indexesの配列長
2962     * @return RES_OK 成功
2963     *
2964     */
2965    static result_t getIndexesInternal( sessionid_t sid, const char *cond, userid_t uid, const index_t **indexes, int *indexesLen, string &criteriaString ){
2966        result_t result;
2967        SQLRETURN sqlcode;
2968        SQLHANDLE hstmt = NULL;    
2969        
2970        string groupUserLinkTable = dbprefix + "_xnpaccount_groups_users_link";
2971        string indexTable = dbprefix + "_xnpaccount_index";
2972        string itemTable = dbprefix + "_xnpaccount_item_basic";
2973        string uidString = unsignedIntToString( uid );
2974        if ( cond == 0 )
2975            cond = " 1 ";
2976        
2977        string accessRightCond = "1";
2978        if ( uid != 0 && !isModerator( sid, uid ) )
2979            accessRightCond =
2980                " (  tx.open_level=1 "
2981                " OR tx.open_level=2 AND tlink.uid is not NULL "
2982                " OR tx.open_level=3 AND tx.uid = " + uidString + ")"; // アクセス権を表すSQL
2983        string sql = "SELECT tx.index_id "
2984            " FROM " + indexTable + " AS tx " +
2985            " LEFT JOIN " + itemTable + " AS ti on tx.index_id = ti.item_id "
2986            " LEFT JOIN " + groupUserLinkTable + " AS tlink on tlink.gid = tx.gid and tlink.uid = " + uidString +
2987            " WHERE " + accessRightCond + " AND " + cond;
2988        SQLINTEGER len = 0;
2989        result = countResultRows( sql.c_str(), &len );
2990        if ( result != RES_OK )
2991            return result;
2992        if ( len == 0 ){
2993            *indexes =  0;
2994            *indexesLen = 0;
2995            return result;
2996        }
2997        
2998        sql = "SELECT tx.index_id, tx.parent_index_id, tx.uid, tx.gid, tx.open_level, tx.sort_number "
2999            " , ti.item_type_id, ti.item_subtype, ti.uid, ti.title, ti.keywords, ti.description, ti.last_update_date, ti.creation_date "
3000            " FROM " + indexTable + " AS tx " +
3001            " LEFT JOIN " + itemTable + " AS ti on tx.index_id = ti.item_id "
3002            " LEFT JOIN " + groupUserLinkTable + " AS tlink on tlink.gid = tx.gid and tlink.uid = " + uidString +
3003            " WHERE " + accessRightCond + " AND " + cond + criteriaString;
3004        SQLCHAR subtype[XNP_ITEM_SUBTYPE_LEN+1], title[XNP_ITEM_TITLE_LEN+1],
3005          keywords[XNP_ITEM_KEYWORDS_LEN+1], description[XNP_ITEM_DESCRIPTION_LEN+1];
3006        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
3007            if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){
3008                SQLUINTEGER xid = 0, parentXID = 0, ownerUID = 0, ownerGID = 0, openLevel = 0,
3009                 sortNumber = 0, itemTypeID = 0, contributorUID = 0, lastUpdateDate = 0, creationDate = 0;
3010                SQLINTEGER lens[14];
3011                SQLBindCol( hstmt, 1, SQL_C_ULONG, &xid, 0, lens+0 );
3012                SQLBindCol( hstmt, 2, SQL_C_ULONG, &parentXID, 0, lens+1 );
3013                SQLBindCol( hstmt, 3, SQL_C_ULONG, &ownerUID, 0, lens+2 );
3014                SQLBindCol( hstmt, 4, SQL_C_ULONG, &ownerGID, 0, lens+3 );
3015                SQLBindCol( hstmt, 5, SQL_C_ULONG, &openLevel, 0, lens+4 );
3016                SQLBindCol( hstmt, 6, SQL_C_ULONG, &sortNumber, 0, lens+5 );
3017                
3018                SQLBindCol( hstmt, 7, SQL_C_ULONG, &itemTypeID, 0, lens+6 );
3019                SQLBindCol( hstmt, 8, SQL_C_CHAR , &subtype, XNP_ITEM_SUBTYPE_LEN+1, lens+7 );
3020                SQLBindCol( hstmt, 9, SQL_C_ULONG, &contributorUID, 0, lens+8 );
3021                SQLBindCol( hstmt,10, SQL_C_CHAR , &title, XNP_ITEM_TITLE_LEN+1, lens+9 );
3022                SQLBindCol( hstmt,11, SQL_C_CHAR , &keywords, XNP_ITEM_KEYWORDS_LEN+1, lens+10 );
3023                SQLBindCol( hstmt,12, SQL_C_CHAR , &description, XNP_ITEM_DESCRIPTION_LEN+1, lens+11 );
3024                SQLBindCol( hstmt,13, SQL_C_ULONG, &lastUpdateDate, 0, lens+12 );
3025                SQLBindCol( hstmt,14, SQL_C_ULONG, &creationDate, 0, lens+13 );
3026                
3027                index_t *index = new index_t[len];
3028                int i = 0;
3029                while ( i < len && ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){
3030                    if ( lens[ 7] == SQL_NULL_DATA ) subtype[0] = '\0';
3031                    if ( lens[ 9] == SQL_NULL_DATA ) title[0] = '\0';
3032                    if ( lens[10] == SQL_NULL_DATA ) keywords[0] = '\0';
3033                    if ( lens[11] == SQL_NULL_DATA ) description[0] = '\0';
3034                    index[i].setIndexID( xid );
3035                    index[i].setParentIndexID( parentXID );
3036                    index[i].setOwnerUID( ownerUID );
3037                    index[i].setOwnerGID( ownerGID );
3038                    index[i].setOpenLevel( openLevel );
3039                    index[i].setSortNumber( sortNumber );
3040                    index[i].setItemTypeID( itemTypeID );
3041                    index[i].setSubtype( (char *)subtype );
3042                    index[i].setContributorUID( contributorUID );
3043                    index[i].setTitle( (char *)title );
3044                    index[i].setKeywords( (char *)keywords );
3045                    index[i].setDescription( (char *)description );
3046                    index[i].setLastUpdateDate( lastUpdateDate );
3047                    index[i].setCreationDate( creationDate );
3048                    i++;
3049                }
3050                *indexes = index;
3051                *indexesLen = i;
3052                result = RES_OK;
3053            }else{
3054                string message( "SQLExecDirect in getIndexesInternal. sql=" );
3055                message = message + sql;
3056                setLastErrorString( message.c_str() );
3057                result = RES_ERROR;
3058            }
3059            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
3060        }
3061        else {
3062            setLastErrorString( "SQLAllocHandle(SQL_HANDLE_STMT,...) in getIndexesInternal" );
3063            result = RES_ERROR;
3064        }
3065        return result;
3066    }
3067    
3068    /**
3069     *
3070     * uidの人が(ownerUID,ownerGID,openLevel)のインデックスに書き込む権限があるかどうか調べる。
3071     *
3072     * @param uid セッションID
3073     * @param ownerUID index所有者
3074     * @param ownerGID index所有グループ
3075     * @param openLevel
3076     * @return RES_OK 成功
3077     *
3078     */
3079    static bool isWritableInternal( sessionid_t sid, userid_t uid, userid_t ownerUID, groupid_t ownerGID, openlevel_t openLevel ){
3080        // todo: 遅いようならisGroupAdmin,isModeratorを使わずになんとかする
3081        if ( openLevel == index::OL_PUBLIC ){
3082        }
3083        else if ( openLevel == index::OL_GROUP_ONLY ){
3084            if ( isGroupAdmin( sid, ownerGID, uid ) )
3085                return true;
3086        }
3087        else if ( openLevel == index::OL_PRIVATE ){
3088            if ( uid == ownerUID )
3089                return true;
3090        }
3091        if ( isModerator(sid, uid) )
3092            return true;
3093        char buf[100];
3094        sprintf( buf, "isWritableInternal: sid=%d uid=%d ownerUID=%d ownerGID=%d openLevel=%d", sid, uid, ownerUID, ownerGID, openLevel );
3095        setLastErrorString( buf );
3096        
3097        return false;
3098    }
3099    
3100    static bool isWritableInternal( sessionid_t sid, userid_t uid, const index_t *index ){
3101        return isWritableInternal( sid, uid, index->getOwnerUID(), index->getOwnerGID(), index->getOpenLevel() );
3102    }
3103    
3104    /**
3105     *
3106     *
3107     * @param sid セッションID
3108     * @param gid グループのUID
3109     * @param uid ユーザのUID
3110     * @return true 管理権限あり
3111     * @return false 管理権限なし,または不明
3112     *
3113     */
3114    bool isGroupMemberInternal( sessionid_t sid, groupid_t gid, userid_t uid )
3115    {
3116        if( hdbc == NULL ) return false;
3117        if( !isValidSessionID( sid ) ) return false;
3118        if( !uidExists( uid ) ) return false;
3119        if( !gidExists( gid ) ) return false;
3120        
3121        bool ret = false;
3122        string sql;
3123        SQLRETURN sqlcode;
3124        SQLINTEGER count = 0;
3125        
3126        sql = "SELECT * FROM " + dbprefix + "_xnpaccount_groups_users_link ";
3127        sql += "WHERE gid=" + string( unsignedIntToString( gid ) );
3128        sql += " AND uid=" + string( unsignedIntToString( uid ) );
3129        if( countResultRows( sql.c_str(), &count ) == RES_OK ){
3130            if( count > 0 ){
3131                ret = true;
3132            }else{
3133                ret = false;
3134            }
3135        }else{
3136            ret = false;
3137        }
3138        return ret;
3139    }
3140    
3141    
3142    /**
3143     *
3144     * uidの人が(ownerUID,ownerGID,openLevel)のインデックスを読み込む権限があるかどうか調べる。
3145     *
3146     * @param uid セッションID
3147     * @param ownerUID index所有者
3148     * @param ownerGID index所有グループ
3149     * @param openLevel
3150     * @return RES_OK 成功
3151     *
3152     */
3153    static bool isReadableInternal( sessionid_t sid, userid_t uid, userid_t ownerUID, groupid_t ownerGID, openlevel_t openLevel ){
3154        // todo: 遅いようならisGroupMemberInternalを使わずになんとかする
3155        if ( openLevel == index::OL_PUBLIC ){
3156            return true;
3157        }
3158        else if ( openLevel == index::OL_GROUP_ONLY ){
3159            if ( isGroupMemberInternal( sid, ownerGID, uid ) )
3160                return true;
3161        }
3162        else if ( openLevel == index::OL_PRIVATE ){
3163            if ( uid == ownerUID )
3164                return true;
3165        }
3166        if ( isModerator(sid, uid) )
3167            return true;
3168        return false;
3169    }
3170    
3171    static bool isReadableInternal( sessionid_t sid, userid_t uid, const index_t *index ){
3172        return isReadableInternal( sid, uid, index->getOwnerUID(), index->getOwnerGID(), index->getOpenLevel() );
3173    }
3174    
3175    
3176    /**
3177     *
3178     * インデックスキーワードへの読み込み権を調べる
3179     *
3180     * @param sid セッションID
3181     * @param xid 取得したいインデックスキーワードのXID
3182     * @return
3183     *
3184     */
3185    bool isIndexReadable( sessionid_t sid,  indexid_t xid )
3186    {
3187        result_t result;
3188        userid_t uid;
3189        if( hdbc == NULL ) return false;
3190        
3191        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3192        if( result == RES_OK ){
3193            string criteria;
3194            string cond( "tx.index_id = " + unsignedIntToString( xid ) );
3195            const index_t *indexes;
3196            int indexesLen;
3197            // todo: 遅いようなら他の方法にする
3198            result = getIndexesInternal( sid, cond.c_str() , uid, &indexes, &indexesLen, criteria );
3199            if ( result == RES_OK ){
3200                freeIndex( indexes );
3201                if ( indexesLen != 0 ){
3202                    return true;
3203                }
3204                else ; // 不正なxid
3205            }
3206        }
3207        return false;
3208    }
3209    
3210    /**
3211     *
3212     * インデックスキーワードへの書き込み権を調べる
3213     *
3214     * @param sid セッションID
3215     * @param xid 取得したいインデックスキーワードのXID
3216     * @return
3217     *
3218     */
3219    bool isIndexWritable( sessionid_t sid,  indexid_t xid )
3220    {
3221        result_t result;
3222        userid_t uid;
3223        if( hdbc == NULL ) return false;
3224        
3225        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3226        if( result == RES_OK ){
3227            string criteria;
3228            string cond( "tx.index_id = " + unsignedIntToString( xid ) );
3229            const index_t *indexes;
3230            int indexesLen;
3231            // todo: 遅いようなら他の方法にする
3232            result = getIndexesInternal( sid, cond.c_str() , uid, &indexes, &indexesLen, criteria );
3233            if ( result == RES_OK ){
3234                bool writable = false;
3235                if ( indexesLen != 0 ){
3236                    writable = isWritableInternal( sid, uid, indexes );
3237                }
3238                freeIndex( indexes );
3239                return writable;
3240            }
3241        }
3242        return false;
3243    }
3244    
3245    
3246    
3247    
3248    /** parentXIDの直下のインデックスのsort_numberの最大値+1 を求める。parentXIDの値が有効かどうかは判断しない。
3249      * ROOT直下はsort_numberの埋め方が異なるので、この関数では処理できない。
3250      * @param parentXID  親index_id
3251      * @param sortNumber max(sort_number)+1
3252      * @return RES_OK 成功
3253      */
3254    result_t getNewSortNumber( indexid_t parentXID, unsigned int *sortNumber ){
3255        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3256        
3257        string sql = "SELECT max(sort_number) FROM " + dbprefix + "_xnpaccount_index WHERE parent_index_id=" + unsignedIntToString(parentXID);
3258        unsigned int u;
3259        result_t result = queryGetUnsignedInt( "getNewSortNumber", sql, &u );
3260        if ( result == RES_OK ){
3261            if ( u == 0 ) // NULLだった。つまりparentXIDはleaf node である。
3262                *sortNumber = 1; // sort_number=0は予約済みなので1から始まる
3263            else
3264                *sortNumber = u+1;
3265        }
3266        return result;
3267    }
3268    
3269    /**
3270     *
3271     * インデックスキーワードを取得する
3272     *
3273     * @param sid セッションID
3274     * @param xid 取得したいインデックスキーワードのXID
3275     * @param index 取得したインデックスキーワード
3276     * @return RES_OK 成功
3277     *
3278     */
3279    result_t getIndex( sessionid_t sid, indexid_t xid, const index_t **index )
3280    {
3281        result_t result;
3282        userid_t uid;
3283        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3284        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3285        if( result == RES_OK ){
3286            string criteriaString;
3287            string cond( " index_id = ");
3288            cond += unsignedIntToString(xid);
3289            int indexesLen;
3290            result = getIndexesInternal( sid, cond.c_str(), uid, index, &indexesLen, criteriaString );
3291            if ( result == RES_OK && indexesLen == 0 ){
3292                string s = "in getIndex: no match " + cond;
3293                setLastErrorString( s.c_str() );
3294                result = RES_ERROR;
3295            }
3296        }
3297        return result;
3298    }
3299    
3300    /**
3301     *
3302     * 全てのインデックスキーワードを得る
3303     *
3304     * @param sid セッションID
3305     * @param cri 結果の範囲指定,ソート条件指定
3306     * @param indexes indexの配列を受け取るポインタ
3307     * @param indexesLen indexes配列の要素数
3308     * @return RES_OK 成功
3309     *
3310     */
3311    result_t getAllIndexes( sessionid_t sid, criteria_t *cri, const index_t **indexes, int *indexesLen )
3312    {
3313        result_t result;
3314        userid_t uid;
3315        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3316        
3317        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3318        if( result == RES_OK ){
3319            string criteriaString = criteria2str( cri );
3320            result = getIndexesInternal( sid, 0, uid, indexes, indexesLen, criteriaString );
3321        }
3322        return result;
3323    }
3324    
3325    /**
3326     *
3327     * 特定のインデックスキーワードの全ての子インデックスキーワードを得る
3328     *
3329     * @param sid セッションID
3330     * @param parentXID このインデックスの子を得る
3331     * @param cri 結果の範囲指定,ソート条件指定
3332     * @param indexes indexの配列を受け取るポインタ
3333     * @param indexesLen indexes配列の要素数
3334     * @return RES_OK 成功
3335     *
3336     */
3337    result_t getIndexes( sessionid_t sid, indexid_t parentXID, criteria_t *cri, const index_t **indexes, int *indexesLen )
3338    {
3339        result_t result;
3340        userid_t uid;
3341        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3342        
3343        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3344        if( result == RES_OK ){
3345            string criteriaString = criteria2str( cri );
3346            string cond( " parent_index_id = ");
3347            cond += unsignedIntToString(parentXID);
3348            
3349            result = getIndexesInternal( sid, cond.c_str(), uid, indexes, indexesLen, criteriaString );
3350        }
3351        return result;
3352    }
3353    
3354    /** インデックスを作成する。引数のチェックは行わない。
3355      */
3356    static result_t insertIndexInternal( sessionid_t sid, index_t *index, indexid_t *xid ){
3357        itemid_t iid;
3358    
3359        result_t result = insertItemInternal( sid, index, &iid );
3360        if ( result == RES_OK ){
3361            string sql = "INSERT INTO " + dbprefix + "_xnpaccount_index ( parent_index_id, uid, gid, open_level, sort_number ) values ( "
3362             + unsignedIntToString(index->getParentIndexID()) + "," + unsignedIntToString(index->getOwnerUID()) + ","
3363             + unsignedIntToString(index->getOwnerGID())  + "," + unsignedIntToString(index->getOpenLevel()) + ","
3364             + unsignedIntToString(index->getSortNumber()) + ") ";
3365            result = querySimple( "insertIndexInternal", sql );
3366            if( result == RES_OK ){
3367                *xid = iid;
3368            }
3369            else {
3370                sql = "DELETE FROM " + dbprefix + "_xnpaccount_item where item_id=" + unsignedIntToString( iid  );
3371                querySimple( "insertIndexInternal", sql );
3372                string message( "in insertIndexInternal: bad uid/gid/openlevel. sql=" );
3373                message = message + sql;
3374                setLastErrorString( message.c_str() );
3375                result = RES_ERROR; // エラー: insertに失敗した
3376            }
3377        }else{
3378            ;
3379        }
3380        return result;
3381    }
3382    
3383    /**
3384     *
3385     * インデックスキーワードを登録する
3386     *
3387     * @param sid セッションID
3388     * @param index 登録するインデックスキーワード
3389     * @param xid 登録したインデックスのItemID
3390     * @return RES_OK 成功
3391     *
3392     */
3393    result_t insertIndex( sessionid_t sid, index_t *index, indexid_t *xid )
3394    {
3395        result_t result = RES_ERROR;
3396        userid_t uid;
3397        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3398        
3399        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3400        if( result != RES_OK ) return result;
3401        
3402        if( index->getParentIndexID() == item::IID_ROOT ||
3403            index->getParentIndexID() == item::IID_BINDERS ){
3404            setLastErrorString( "in insertIndex: parentXID must not Root/Binders " );
3405            return RES_ERROR; // エラー: Root/Binders直下には作成できない。
3406        }
3407        
3408        // parentXID のアクセス権を調べる
3409        SQLRETURN sqlcode;
3410        SQLHANDLE hstmt = NULL;    
3411        string sql;
3412        
3413        SQLINTEGER cbTitle = SQL_NTS, parentXIDInd = 0;
3414        SQLINTEGER parentXID = index->getParentIndexID();
3415        string indexTable = dbprefix + "_xnpaccount_index";
3416        string itemTable = dbprefix + "_xnpaccount_item_basic";
3417        sql = "SELECT tx_parent.uid, tx_parent.gid, tx_parent.open_level, tx_child.index_id, ti_child.item_id "
3418            " FROM " + indexTable + " AS tx_parent "
3419            " LEFT JOIN " + indexTable + " AS tx_child ON tx_child.parent_index_id = tx_parent.index_id "
3420            " LEFT JOIN " + itemTable  + " AS ti_child ON tx_child.index_id = ti_child.item_id AND ti_child.title = ? AND ti_child.item_type_id = " + unsignedIntToString( item::ITID_INDEX ) +
3421            " WHERE tx_parent.index_id = ? ";
3422        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
3423            SQLBindParameter(hstmt,  1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, XNP_ITEM_TITLE_LEN, 0, (SQLCHAR *)index->getTitle(), strlen(index->getTitle()), &cbTitle );
3424            SQLBindParameter(hstmt,  2, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 10, 0, &parentXID, 0, &parentXIDInd );
3425            if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){
3426                SQLUINTEGER parentUID = 0, parentGID = 0, parentOpenLevel = 0, childXID = 0, childItemID = 0;
3427                SQLINTEGER len1, len2, len3, len4, len5;
3428                SQLBindCol( hstmt, 1, SQL_C_ULONG, &parentUID, 0, &len1 );
3429                SQLBindCol( hstmt, 2, SQL_C_ULONG, &parentGID, 0, &len2 );
3430                SQLBindCol( hstmt, 3, SQL_C_ULONG, &parentOpenLevel, 0, &len3 );
3431                SQLBindCol( hstmt, 4, SQL_C_ULONG, &childXID, 0, &len4 );
3432                SQLBindCol( hstmt, 5, SQL_C_ULONG, &childItemID, 0, &len5 );
3433                if( ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){
3434                    if ( len5 != SQL_NULL_DATA ){
3435                        setLastErrorString( "in insertIndex: title conflict" );
3436                        result = RES_ERROR;  // エラー: 同名の子indexが既に存在する。
3437                    }
3438                    else if ( !isWritableInternal( sid, uid, parentUID, parentGID, parentOpenLevel ) ){
3439                        setLastErrorString( "in insertIndex: cannot write to parentindex" );
3440                        result = RES_ERROR;  // エラー: 親インデックスへの書き込み権限が無い。
3441                    }
3442    /*                else if ( !(parentOpenLevel == index::OL_PUBLIC     && index->getOpenLevel() == index::OL_PUBLIC)
3443                           && !(parentOpenLevel == index::OL_GROUP_ONLY && index->getOpenLevel() == index::OL_GROUP_ONLY && parentGID == index->getOwnerGID())
3444                           && !(parentOpenLevel == index::OL_PRIVATE    && index->getOpenLevel() == index::OL_PRIVATE    && parentUID == index->getOwnerUID())) {
3445                        setLastErrorString( "in insertIndex: bad uid/gid/openlevel" );
3446                        result = RES_ERROR; // エラー: 親インデックスとアクセス権限が矛盾する。
3447                    }
3448    */
3449                    else {
3450                        unsigned int sortNumber;
3451                        result = getNewSortNumber( index->getParentIndexID(), &sortNumber );
3452                        if ( result == RES_OK ){
3453                            index->setOpenLevel( parentOpenLevel );
3454                            index->setOwnerGID( parentGID );
3455                            index->setOwnerUID( parentUID );
3456                            index->setSortNumber( sortNumber );
3457                            // インデックスを作成する。
3458                            result = insertIndexInternal( sid, index, xid );
3459                        }
3460                        else {
3461                            ; // error: getNewSortNumber failed.
3462                        }
3463                    }
3464                }else {
3465                    string s( "SQLFetch in insertIndex sql=" );
3466                    s += sql;
3467                    s += " sqlcode=" + intToString(sqlcode);
3468                    s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
3469                    setLastErrorString( s.c_str() );
3470                    result = RES_ERROR;
3471                }
3472            }else{
3473                string s( "in insertIndex: SQLExecDirect failed. sql=" );
3474                s += sql;
3475                s += " sqlcode=" + intToString(sqlcode);
3476                s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
3477                setLastErrorString( s.c_str() );
3478                result = RES_ERROR;
3479            }
3480            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
3481        }
3482        else {
3483            setLastErrorString( "SQLAllocHandle in insertIndex" );
3484            result = RES_ERROR;
3485        }
3486        return result;
3487    }
3488    
3489    /** 内部で使用。xidの子孫(xidを含む)のインデックスのIDを全て取得する。delete[] *descndexIDが必要。
3490      * @param xid 対象
3491      * @param descXID 子孫を受け取る配列
3492      * @param descXIDLen descXIDの配列長
3493      */
3494    result_t getDescendantIndexID( int xid, indexid_t **descXID, int *descXIDLen ){
3495        // todo
3496        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3497        
3498        string cond = "select count(*) from " + dbprefix + "_xnpaccount_index";
3499        unsigned int allIndexCount;
3500        result_t result = queryGetUnsignedInt( "getDescendantIndexID", cond, &allIndexCount );
3501        if ( result != RES_OK )
3502            return result;
3503        
3504        indexid_t *p0 = new indexid_t[allIndexCount+1];
3505        indexid_t *p = p0;
3506        indexid_t *pFill = p0;
3507        indexid_t *pEnd = p0 + allIndexCount+1;
3508        *pFill++ = xid;
3509        /* *p 〜 *(pFill-1) : 未探索ノード。これが空になると探索終了。
3510           *pFill 〜 *(pEnd-1) : 空き領域。これが空になると、それ以上探索できないので異常終了
3511        */
3512        SQLRETURN sqlcode;
3513        SQLHANDLE hstmt = NULL;    
3514        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
3515            result = RES_ERROR;
3516            while ( true ) {
3517                if ( p == pFill ){
3518                    // もう未探索ノードが無い。
3519                    result = RES_OK;
3520                    break;
3521                }
3522                else if ( pFill == pEnd ){
3523                    // これ以上探索できない。
3524                    result = RES_ERROR;
3525                    setLastErrorString( "getDescendantIndexID: index buffer overflow." );
3526                    break;
3527                }
3528                
3529                xid = *p++; // 未探索ノードを1つ取り出す
3530                
3531                // 子を列挙して未探索ノードとして追加。
3532                string sql = "SELECT index_id FROM " + dbprefix + "_xnpaccount_index WHERE parent_index_id=" + unsignedIntToString(xid);
3533                if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){
3534                    SQLUINTEGER sXID = 0;
3535                    SQLINTEGER len;
3536                    SQLBindCol( hstmt, 1, SQL_C_ULONG, &sXID, 0, &len );
3537                    while ( ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){
3538                        *pFill++ = sXID;
3539                        if ( pFill == pEnd )
3540                            break;
3541                    }
3542                }else{
3543                    setLastErrorString( "SQLExecDirect in getDescendantIndexID" );
3544                    result = RES_ERROR;
3545                    break;
3546                }
3547            }
3548            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
3549        }else{
3550            setLastErrorString( "SQLAllocHandle in getDescendantIndexID" );
3551            result = RES_ERROR;
3552        }
3553        
3554        if ( result == RES_OK ){
3555            *descXID = p0;
3556            *descXIDLen = pFill - p0;
3557        }
3558        else {
3559            delete[] p0;
3560        }
3561        return result;
3562    }
3563    
3564    
3565    #if 0
3566    typedef struct {
3567       indexid_t xid;
3568       indexid_t parentXID;
3569       int tag;
3570    } indexLink_t;
3571    /** 全ての(index_id,parent_index_id)を列挙する。
3572     * @param links     配列。RES_OKを返した場合のみ有効。delete[]が必要。
3573     * @param linksLen  linksの配列長
3574     */
3575    static result_t getAllIndexLink( indexLink_t **links, int *linksLen ){
3576        result_t result = RES_OK;
3577        string sql("SELECT COUNT(*) from " + dbprefix + "_xnpaccount_index");
3578        int totalIndexCount;
3579        result = getCountInternal( "getAllIndexLink", sql, &totalIndexCount );
3580        
3581        if ( result != RES_OK ){
3582            return result;
3583        }
3584        
3585        SQLRETURN sqlcode;
3586        SQLHANDLE hstmt = NULL;    
3587        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
3588            string sql("SELECT index_id, parent_index_id FROM " + dbprefix + "_xnpaccount_index ORDER BY index_id");
3589            if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){
3590                SQLUINTEGER xid = 0, parentXID = 0;
3591                SQLINTEGER len1, len2;
3592                SQLBindCol( hstmt, 1, SQL_C_ULONG, &xid, 0, &len1 );
3593                SQLBindCol( hstmt, 2, SQL_C_ULONG, &parentXID, 0, &len2 );
3594                
3595                indexLink_t *p = new indexLink_t[totalIndexCount];
3596                int i = 0;
3597                while ( ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){
3598                    if ( i == totalIndexCount ){
3599                        result = RES_ERROR;
3600                        break;
3601                    }
3602                    p[i].xid = xid;
3603                    p[i].parentXID = parentXID;
3604                    p[i].tag = 0;
3605                    i++;
3606                }
3607                if ( result == RES_OK ){
3608                    *links = p;
3609                    *linksLen = i;
3610                    
3611                    for (
3612                    
3613                }
3614            }
3615            else {
3616                setLastErrorString( "SQLExecDirect in getAllIndexLink" );
3617                result = RES_ERROR;
3618            }
3619            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
3620        }else{
3621            setLastErrorString( "SQLAllocHandle(SQL_HANDLE_STMT,...) in getAllIndexLink" );
3622            result = RES_ERROR;
3623        }
3624        return result;
3625    }
3626    #endif
3627    
3628    
3629    result_t updateIndexInternal( sessionid_t sid, userid_t uid, index_t *newIndex, const index_t *oldIndex, const index_t *newParentIndex, const index_t *oldParentIndex ){
3630        bool move = ( newIndex->getParentIndexID() != oldIndex->getParentIndexID() );
3631        
3632        result_t result = RES_ERROR;
3633        if ( ( oldIndex->getItemID() == item::IID_ROOT || oldIndex->getParentIndexID() == item::IID_ROOT || oldIndex->getIndexID() == item::IID_BINDERS )
3634          && ( newIndex->getOwnerUID() != oldIndex->getOwnerUID()
3635              || newIndex->getOwnerGID() != oldIndex->getOwnerGID()
3636              || newIndex->getOpenLevel() != oldIndex->getOpenLevel() ) ){
3637                setLastErrorString( "in updateIndex: cannot change owner/openlevel of system-created-index" );
3638                return RES_ERROR; // Root/Binder/Public/Group/Privateの公開属性を書き換えることはできない。
3639        }
3640        
3641        // parent_index_idを書き換える場合は更に面倒なエラーチェックを行う
3642        if ( move ){
3643            if ( oldIndex->getItemID() == item::IID_ROOT || oldIndex->getParentIndexID() == item::IID_ROOT ){
3644                setLastErrorString( "in updateIndex: cannot change parent_index_id of system-created-index" );
3645                return RES_ERROR; //自分がRootあるいは親がRootの場合は、親XIDを変更できない。
3646            }
3647            else if ( newIndex->getParentIndexID() == item::IID_BINDERS ){
3648                setLastErrorString( "in updateIndex: parent_index_id must not be BINDERS" );
3649                return RES_ERROR; // 親をBinderにすることはできない。
3650            }
3651            else if ( newIndex->getParentIndexID() == item::IID_ROOT ){
3652                setLastErrorString( "in updateIndex: cannot change parent_index_id to ROOT" );
3653                return RES_ERROR; // 親がRootでないなら、親をRootにすることはできない。
3654            }
3655        }
3656        
3657        // 親ディレクトリに書き込めるかどうかチェック
3658        if ( !isWritableInternal( sid, uid, newParentIndex ) ){
3659            setLastErrorString( "in updateIndex: no access right. cannot move." );
3660            return RES_ERROR; // 移動先への書き込み権限が必要。
3661        }
3662        
3663        // sort_numberを変更する場合は重複が無いかどうかチェック。ただし移動する場合は自動でsort_numberが振られるのでチェックしない
3664        if ( !move && newIndex->getSortNumber() != oldIndex->getSortNumber() ){
3665            const index_t *conflictIndexes;
3666            int conflictIndexesLen;
3667            string criteriaString;
3668            string cond("tx.sort_number=" + unsignedIntToString(newIndex->getSortNumber()));
3669            result = getIndexesInternal( sid, cond.c_str() , 0, &conflictIndexes, &conflictIndexesLen, criteriaString );
3670            if ( result == RES_OK ){
3671                freeIndex( conflictIndexes );
3672                if ( conflictIndexesLen != 0 ){
3673                    setLastErrorString( "in updateIndex: sort_number conflicts" );
3674                    return RES_ERROR; // sortNumberの重複あり
3675                }
3676                else {
3677                    ;// sortNumberの重複無し。
3678                }
3679            }
3680            else {
3681                return result; // cannot getIndexesInternal()
3682            }
3683        }
3684        
3685    
3686        if ( move ){
3687            indexid_t *descXID = 0;
3688            int descXIDLen;
3689            
3690            // 子孫のxidを列挙。循環のチェックを行う。
3691            result = getDescendantIndexID( oldIndex->getIndexID(), &descXID, &descXIDLen );
3692            if ( result != RES_OK ){
3693                setLastErrorString( "in updateIndexInternal: getDescendantIndexID failed" );
3694                return RES_ERROR;
3695            }
3696            int i;
3697            for ( i = 0; i < descXIDLen; i++ ){
3698                if ( descXID[i] == newIndex->getParentIndexID() ){
3699                    break;
3700                }
3701            }
3702            freeIndexID( descXID );
3703            if ( i != descXIDLen ){
3704                // 子孫のIndexを親にしようとした
3705                setLastErrorString( "in updateIndexInternal: circular parent" );
3706                return RES_ERROR;
3707            }
3708            
3709            // sort_numberを生成する。
3710            unsigned int sortNumber;
3711            result = getNewSortNumber(newParentIndex->getIndexID(), &sortNumber);
3712            if ( result != RES_OK ){
3713                return result;
3714            }
3715            newIndex->setSortNumber( sortNumber );
3716        }
3717    
3718        SQLRETURN sqlcode;
3719        SQLHANDLE hstmt = NULL;    
3720        if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
3721            string ownerUIDString = "0";
3722            string ownerGIDString = "0";
3723            if ( newIndex->getOwnerUID() != 0 ) ownerUIDString = unsignedIntToString(newIndex->getOwnerUID());
3724            if ( newIndex->getOwnerGID() != 0 ) ownerGIDString = unsignedIntToString(newIndex->getOwnerGID());
3725            string sql("UPDATE " +  dbprefix + "_xnpaccount_index set"
3726                " parent_index_id = " + unsignedIntToString(newIndex->getParentIndexID()) +
3727                ", uid = " + ownerUIDString +
3728                ", gid = " + ownerGIDString +
3729                ", open_level = " + unsignedIntToString(newIndex->getOpenLevel()) +
3730                ", sort_number = " + unsignedIntToString(newIndex->getSortNumber()) +
3731                " where index_id = " + unsignedIntToString(newIndex->getIndexID()) );
3732            
3733            if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){
3734                string sql("UPDATE " +  dbprefix + "_xnpaccount_item_basic set"
3735                    " item_type_id = " + unsignedIntToString(newIndex->getItemTypeID()) +
3736                    ", uid = " + unsignedIntToString(newIndex->getContributorUID()) +
3737                    ", last_update_date = " + unsignedIntToString(newIndex->getLastUpdateDate()) +
3738                    ", creation_date = " + unsignedIntToString(newIndex->getCreationDate()) +
3739                    ", item_subtype = ?, title = ?, keywords = ?, description = ? "
3740                    " where item_id = " + unsignedIntToString(newIndex->getIndexID()) );
3741                sqlcode = SQLPrepare(hstmt, (SQLCHAR*)sql.c_str(), SQL_NTS);
3742                if( sqlcode == SQL_SUCCESS || sqlcode == SQL_SUCCESS_WITH_INFO ){
3743                    SQLINTEGER cb1 = SQL_NTS, cb2 = SQL_NTS, cb3 = SQL_NTS, cb4 = SQL_NTS;
3744                    SQLBindParameter(hstmt,  1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, XNP_ITEM_SUBTYPE_LEN, 0, (SQLCHAR *)newIndex->getSubtype(), 0, &cb1 );
3745                    SQLBindParameter(hstmt,  2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, XNP_ITEM_TITLE_LEN, 0, (SQLCHAR *)newIndex->getTitle(), 0, &cb2 );
3746                    SQLBindParameter(hstmt,  3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, XNP_ITEM_KEYWORDS_LEN, 0, (SQLCHAR *)newIndex->getKeywords(), 0, &cb3 );
3747                    SQLBindParameter(hstmt,  4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, XNP_ITEM_DESCRIPTION_LEN, 0, (SQLCHAR *)newIndex->getDescription(), 0, &cb4 );
3748                    if( ( sqlcode = SQLExecute( hstmt ) ) == SQL_SUCCESS ){
3749                        result = RES_OK;
3750                    }
3751                    else {
3752                        string s( "SQLExecDirect in updateIndex ");
3753                        s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
3754                        s += string( ", sql=" ) + string( sql );
3755                        setLastErrorString( s.c_str( ) );
3756                        result = RES_ERROR;
3757                    }
3758                }
3759                else {
3760                    setLastErrorString( "SQLPrepare in updateIndex " );
3761                    result = RES_ERROR;
3762                }
3763            }
3764            else {
3765                string s( "SQLExecDirect in updateIndex ");
3766                s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
3767                s += string( ", sql=" ) + string( sql );
3768                setLastErrorString( s.c_str( ) );
3769                result = RES_ERROR;
3770            }
3771            
3772            SQLFreeHandle( SQL_HANDLE_STMT, hstmt );
3773        }
3774        else {
3775            string s( "SQLAllocHandle in updateIndex ");
3776            s += odbcDiagString( SQL_HANDLE_STMT, hstmt, sqlcode );
3777            setLastErrorString( s.c_str( ) );
3778            result = RES_ERROR;
3779        }
3780        
3781        if ( result == RES_OK ){
3782            if ( newParentIndex->getOwnerUID() != oldParentIndex->getOwnerUID()
3783              || newParentIndex->getOwnerGID() != oldParentIndex->getOwnerGID()
3784              || newParentIndex->getOpenLevel() != oldParentIndex->getOpenLevel() ){
3785                //  親の公開領域が変わる場合は、このインデックスとその子孫の公開領域も変わる。
3786                indexid_t *descXID = 0;
3787                int descXIDLen;
3788                result = getDescendantIndexID( oldIndex->getIndexID(), &descXID, &descXIDLen );
3789                if ( result != RES_OK ){
3790                    setLastErrorString( "in updateIndexInternal: getDescendantIndexID failed" );
3791                    return RES_ERROR;
3792                }
3793                int i;
3794                for ( i = 0; i < descXIDLen; i++ ){
3795                    string sql = "UPDATE " + dbprefix + "_xnpaccount_index set "
3796                      " uid=" + unsignedIntToString(newParentIndex->getOwnerUID()) +
3797                      ", gid=" + unsignedIntToString(newParentIndex->getOwnerGID()) +
3798                      ", open_level=" + unsignedIntToString(newParentIndex->getOpenLevel()) +
3799                      " WHERE index_id=" + unsignedIntToString(descXID[i]);
3800                    querySimple( "updateIndex", sql );
3801                }
3802                freeIndexID( descXID );
3803                
3804                // todo: インデックスが移動した場合は、アイテムの所有者に何らかの通知を行う。
3805            }
3806        }
3807        
3808        return result;
3809    
3810    }
3811    
3812    /**
3813     *
3814     * インデックスキーワードを変更する
3815     *
3816     * @param sid セッションID
3817     * @param index 変更するインデックスキーワード
3818     * @return RES_OK 成功
3819     *
3820     */
3821    /*
3822    親XIDを書き換えるときは、以下の点に注意。
3823    親インデックスへの書き込み権限が必要。
3824    
3825    自分またはその子孫のXIDを親XIDとして設定することはできない
3826    親の公開領域が変わる場合は、このインデックスとその子孫の公開領域も変わる。
3827    */
3828    result_t updateIndex( sessionid_t sid, index_t *newIndex )
3829    {
3830        result_t result;
3831        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3832        
3833        userid_t uid;
3834        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3835        if( result != RES_OK ) return result;
3836        
3837        const index_t *oldIndex;
3838        result = getIndex( sid, newIndex->getIndexID(), &oldIndex );
3839        if ( result == RES_OK ){
3840            const index_t *newParentIndex;
3841            result = getIndex( sid, newIndex->getParentIndexID(), &newParentIndex );
3842            if ( result == RES_OK ){
3843                const index_t *oldParentIndex;
3844                result = getIndex( sid, oldIndex->getParentIndexID(), &oldParentIndex );
3845                if ( result == RES_OK ){
3846                    // oldIndex, newParentIndex, oldParentIndexの開放忘れを防ぐ為の2重構造
3847                    result = updateIndexInternal( sid, uid, newIndex, oldIndex, newParentIndex, oldParentIndex );
3848                    freeIndex( oldParentIndex );
3849                }
3850                else {
3851                    ;
3852                }
3853                freeIndex( newParentIndex );
3854            }
3855            else {
3856                ;
3857            }
3858            freeIndex( oldIndex );
3859        }
3860        else {
3861            ;
3862        }
3863        
3864        return result;
3865    }
3866    
3867    /**
3868     *
3869     * インデックスキーワードを削除する
3870     *
3871     * @param sid セッションID
3872     * @param xid 削除するインデックスキーワード
3873     * @return RES_OK 成功
3874     *
3875     */
3876    result_t deleteIndex( sessionid_t sid,  indexid_t xid )
3877    {
3878        char *functionName = "deleteIndex";
3879        result_t result;
3880        userid_t uid;
3881        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3882        
3883        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3884        if( result != RES_OK ) return result;
3885        
3886        const index_t *index;
3887        result = getIndex( sid, xid, &index );
3888        if ( result != RES_OK )
3889            return result;
3890        
3891        if ( index->getIndexID() == item::IID_ROOT || index->getParentIndexID() == item::IID_ROOT ){
3892            result = RES_ERROR;
3893            setLastErrorString( "in deleteIndex: cannot delete system-created-index." );
3894        }
3895        else {
3896            if ( !isWritableInternal( sid, uid, index ) ){
3897                result = RES_NO_WRITE_ACCESS_RIGHT;
3898                setLastErrorString( "in deleteIndex: no write access right." );
3899            }
3900            else {
3901                // 削除対象を列挙
3902                indexid_t *descXID = 0;
3903                int descXIDLen;
3904                result = getDescendantIndexID( xid, &descXID, &descXIDLen );
3905                if ( result != RES_OK ){
3906                    setLastErrorString( "in deleteIndex: getDescendantIndexID failed" );
3907                    return RES_ERROR;
3908                }
3909                
3910                SQLRETURN sqlcode;
3911                SQLHANDLE hstmt = NULL;    
3912                if( ( sqlcode = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ) == SQL_SUCCESS ) {
3913                    result = RES_OK;
3914                    string strParentXID = unsignedIntToString( index->getParentIndexID() );
3915                    for ( int i = descXIDLen-1; i >= 0; i-- ){ // 逆方向にして、途中で失敗した場合の惨事を防ぐ。descXIDは幅優先探索順に並んでいるので。
3916                        string strXID = unsignedIntToString(descXID[i]);
3917                        string linkTable = dbprefix + "_index_item_link";
3918                        
3919                        // descXID[i] のアイテムの中で、被参照数が1のものを列挙 → 迷子にならないように移動
3920                        // HAVINGを使うべき? "select item_id, count(*) as v1, sum(index_id=$index_id) as v2 from $link_table having v1=1 and v2=1"
3921                        // しかしsum内に<predicate>を書いてもいいのだろうか? 代わりにnullifとcoalesceならsqliteは通るが・・・
3922                        //  → 互換性の高い方法がよくわからないので、とりあえず自己joinにする。
3923                        string sql( "SELECT t1.index_item_link_id "
3924                            " FROM " + linkTable + " AS t1 "
3925                            " LEFT JOIN " + linkTable + " AS t2 ON t1.item_id = t2.item_id and t2.index_id <> t1.index_id "
3926                            " WHERE t1.index_id=" + strXID + " and t2.item_id is NULL" );
3927                        if( ( sqlcode = SQLExecDirect( hstmt, (SQLCHAR*)sql.c_str(), sql.length() ) ) == SQL_SUCCESS ){
3928                            SQLUINTEGER sLinkID = 0;
3929                            SQLINTEGER len;
3930                            SQLBindCol( hstmt, 1, SQL_C_ULONG, &sLinkID, 0, &len );
3931                            while ( ( sqlcode = SQLFetch( hstmt ) ) == SQL_SUCCESS ){
3932                                string sql2( "UPDATE " + linkTable + " set index_id= " + strParentXID + " where index_item_link_id = " + unsignedIntToString(sLinkID) );
3933                                result = querySimple( "deleteIndex", sql2 );
3934                                if( result != RES_OK )
3935                                    break;
3936                            }
3937                            if ( result != RES_OK )
3938                                break;
3939                        }else{
3940                            setLastErrorString( "SQLExecDirect in getDescendantIndexID" );
3941                            result = RES_ERROR;
3942                            break;
3943                        }
3944                        
3945                        // descXID[i] のアイテムを全て削除
3946                        sql = "DELETE from " + linkTable + " where index_id=" + strXID;
3947                        result = querySimple( functionName, sql );
3948                        if ( result == RES_OK ){
3949                            // descXID[i] を削除
3950                            sql = "DELETE from " + dbprefix + "_item_basic where item_id =" + strXID;
3951                            result = querySimple( functionName, sql );
3952                            if ( result == RES_OK ){
3953                                sql = "DELETE from " + dbprefix + "_index      where index_id=" + strXID;
3954                                result = querySimple( functionName, sql );
3955                            }
3956                        }
3957                    }
3958                }else{
3959                    setLastErrorString( "SQLAllocHandle in deleteIndex" );
3960                    result = RES_ERROR;
3961                }
3962                
3963                freeIndexID( descXID );
3964            }
3965        }
3966        return result;
3967    }
3968    
3969    
3970    
3971    
3972    
3973    /**
3974     *
3975     * インデックスキーワードの順番を入れ替える
3976     *
3977     * @param sid セッションID
3978     * @param xid1 入れ替えたいインデックスキーワードのXID
3979     * @param xid2 入れ替えたいインデックスキーワードのXID
3980     * @return RES_OK 成功
3981     *
3982     */
3983    result_t swapIndexSortNumber( sessionid_t sid, itemid_t xid1, itemid_t xid2 )
3984    {
3985        char *functionName = "swapIndexSortNumber";
3986        result_t result;
3987        userid_t uid;
3988        if( hdbc == NULL ) return RES_DB_NOT_INITIALIZED;
3989        
3990        result = sessionID2UID( sid, &uid ); // sid から uid を得る
3991        if( result == RES_OK ){
3992            /*
3993              xid1, xid2 の親が異なるなら、エラー。
3994              xid1, xid2 の両方に書き込み権限があることを確認。
3995              操作順序は、
3996                tmp1 = x1.sort_number;
3997                tmp2 = x2.sort_number;
3998                x1.sort_number = 0; // (parent_index_id,sort_number)はuniqueなので、一旦1に変更する。
3999                x2.sort_number = tmp1;
4000                x1.sort_number = tmp2;
4001            */
4002            const index_t *index1, *index2;
4003            result = getIndex( sid, xid1, &index1 );
4004            if ( result == RES_OK ){
4005                result = getIndex( sid, xid2, &index2 );
4006                if ( result == RES_OK ){
4007                    if ( index1->getParentIndexID() == index2->getParentIndexID() ){
4008                        if ( isWritableInternal( sid, uid, index1 ) && isWritableInternal( sid, uid, index2 ) ){
4009                            string xid1String = unsignedIntToString(xid1);
4010                            string xid2String = unsignedIntToString(xid2);
4011                            string indexTable = dbprefix + "_xnpaccount_index";
4012                            string sql1 = "UPDATE " + indexTable + " set sort_number=0 WHERE index_id=" + xid1String;
4013                            string sql2 = "UPDATE " + indexTable + " set sort_number=" + unsignedIntToString(index1->getSortNumber()) + " WHERE index_id=" + xid2String;
4014                            string sql3 = "UPDATE " + indexTable + " set sort_number=" + unsignedIntToString(index2->getSortNumber()) + " WHERE index_id=" + xid1String;
4015                            
4016                            if( (result=querySimple(functionName,sql1)) == RES_OK ){
4017                                if( (result=querySimple(functionName,sql2)) == RES_OK ){
4018                                    if( (result=querySimple(functionName,sql3)) == RES_OK ){
4019                                        ;
4020                                    }
4021                                }
4022                            }
4023                        }
4024                        else {
4025                            setLastErrorString( "swapIndexSortNumber: not writable" );
4026                            result = RES_ERROR;
4027                        }
4028                    }
4029                    else {
4030                        setLastErrorString( "swapIndexSortNumber: not brother" );
4031                        result = RES_ERROR;
4032                    }
4033                    freeIndex( index2 );
4034                }
4035                else {
4036                }
4037                freeIndex( index1 );
4038            }
4039            else {
4040            }
4041        }
4042        return result;
4043    }
4044    
4045    
4046    
4047    void freeIndex( const index_t* ptr ){ delete[] ptr; }
4048    void freeIndexID( const indexid_t* ptr ){ delete[] ptr; }
4049    
4050    
4051  void freeAccount( const account_t* ptr ){ delete[] ( account_t* )ptr; }  void freeAccount( const account_t* ptr ){ delete[] ( account_t* )ptr; }
4052  void freeGroup( const group_t* ptr ){ delete[] ( group_t* )ptr; }  void freeGroup( const group_t* ptr ){ delete[] ( group_t* )ptr; }
4053  void freeSession( const session_t* ptr ){ delete[] ( session_t* )ptr; }  void freeSession( const session_t* ptr ){ delete[] ( session_t* )ptr; }

Legend:
Removed from v.1.36  
changed lines
  Added in v.1.37

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26