void ObjectManipulator::groupObjects()
{
    if (getCurrentObjectTree()->getNumSelected()==0) return;

    FWObject *co = getCurrentObjectTree()->getSelectedObjects().front();

    newGroupDialog ngd(this, m_project->db());

    if (ngd.exec()==QDialog::Accepted)
    {
        QString objName = ngd.m_dialog->obj_name->text();
        QString libName = ngd.m_dialog->libs->currentText();

        QString type = ObjectGroup::TYPENAME;
        if (Service::cast(co)!=NULL)  type=ServiceGroup::TYPENAME;
        if (Interval::cast(co)!=NULL) type=IntervalGroup::TYPENAME;

        FWObject *parent = NULL;
        FWObject *newgrp = NULL;

        list<FWObject*> ll = m_project->db()->getByType( Library::TYPENAME );
        for (FWObject::iterator i=ll.begin(); i!=ll.end(); i++)
        {
            FWObject *lib=*i;
            if (libName==QString::fromUtf8(lib->getName().c_str()))
            {
/* TODO: need to show a dialog and say that chosen library is
 * read-only.  this is not critical though since newGroupDialog fills
 * the pull-down only with names of read-write libraries
 */
                if (lib->isReadOnly()) return;
                parent = FWBTree().getStandardSlotForObject(lib,type);
                if (parent==NULL)
                {
                    if (fwbdebug)
                        qDebug("ObjectManipulator::groupObjects(): could not find standard slot for object of type %s in library %s",
                               type.toAscii().constData(),lib->getName().c_str());
                    return;
                }
                newgrp = m_project->db()->create(type.toStdString());
                newgrp->setName(string(objName.toUtf8().constData()));
                break;
            }
        }
        if (newgrp==NULL) return;

        FWCmdAddObject *cmd = new FWCmdAddObject(
            m_project, parent, newgrp, QObject::tr("Create new group"));
        FWObject *new_state = cmd->getNewState();
        new_state->add(newgrp);

        vector<FWObject*> so = getCurrentObjectTree()->getSimplifiedSelection();
        for (vector<FWObject*>::iterator i=so.begin();  i!=so.end(); ++i)
            newgrp->addRef(*i);

        m_project->undoStack->push(cmd);
    }
}
bool RoutingCompiler::ConvertToAtomicForDST::processNext()
{
    RoutingRule *rule=getNext(); if (rule==NULL) return false;

    //RuleElementSrc *src=rule->getSrc();    assert(src);
    RuleElementRDst *dst=rule->getRDst();    assert(dst);

    for (FWObject::iterator it=dst->begin(); it!=dst->end(); ++it)
    {
        RoutingRule *r = compiler->dbcopy->createRoutingRule();
        r->duplicate(rule);
        compiler->temp_ruleset->add(r);

        FWObject *s = r->getRDst(); assert(s);
        s->clearChildren();
        s->addRef(FWReference::getObject(*it));

        tmp_queue.push_back(r);
    }

    return true;
}
int main(int argc, char * const *argv)
{   
    operands ops;

    string objtype;
    string name;
    string object;
    string group;
    string parent;
    string comment_txt;
    bool list_children = false;
    bool recursive = false;
    string list_format = "%path%";
    bool full_dump = false;
    string import_config;
    bool deduplicate = false;

    if (argc<=1)
    {
        usage();
        exit(1);
    }

    /*
     *  Command line format:
     *  fwbedit command [options]
     *
     *  argv[1] is always command
     */
    cmd_str = string(argv[1]);

    cmd = NONE;
    if (cmd_str=="new") cmd = NEWOBJECT;
    if (cmd_str=="delete") cmd = DELOBJECT;
    if (cmd_str=="modify") cmd = MODOBJECT;
    if (cmd_str=="add") cmd = ADDGRP;
    if (cmd_str=="remove") cmd = REMGRP;
    if (cmd_str=="list") cmd = LIST;
    if (cmd_str=="upgrade") cmd = UPGRADE;
    if (cmd_str=="checktree") cmd = STRUCT;
    if (cmd_str=="merge") cmd = MERGE;
    if (cmd_str=="import") cmd = IMPORT;

    char * const *args = argv;
    args++;
    argc--;

    int   opt;

    switch (cmd)
    {
    case NEWOBJECT:
    {
        // -f file.fwb -t objtype -n name -c comment -p parent [-a attrs]
        while( (opt=getopt(argc, args, "f:t:n:c:p:a:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 't': objtype = optarg; break;
            case 'n': name = optarg; break;
            case 'c': comment_txt = optarg; break;
            case 'p': parent = optarg; break;
            case 'a':
                int num=0;
                if (optarg!=NULL)
                {
                    string str = optarg;
                    num = splitStr(',', str, &ops);
                }
                break;
            }
        }

        if (filename=="")
        {
            usage_new();
            exit(1);
        }

        break;
    }

    case DELOBJECT:
        // -f file.fwb -o object_def
        // object_def can be either full path or object ID
        while( (opt=getopt(argc, args, "f:o:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 'o': object = optarg; break;
            }
        }

        if (filename.empty() || object.empty())
        {
            usage_delete();
            exit(1);
        }

        break;

    case MODOBJECT:
    {
        // -f file.fwb -o object -c comment [-a attrs]
        while( (opt=getopt(argc, args, "f:o:c:a:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 'o': object = optarg; break;
            case 'c': comment_txt = optarg; break;
            case 'a':
                int num=0;
                if (optarg!=NULL)
                {
                    string str = optarg;
                    num = splitStr(',', str, &ops);
                }
                break;
            }
        }

        if (filename.empty() || object.empty())
        {
            usage_modify();
            exit(1);
        }

        break;
    }

    case ADDGRP:
    case REMGRP:
        // -f file.fwb -p group -o object
        // Add/remove object to group
        // both group and object can be either path or ID
        while( (opt=getopt(argc, args, "f:g:o:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 'g': group = optarg; break;
            case 'o': object = optarg; break;
            }
        }

        if (filename.empty() || group.empty() || object.empty())
        {
            if (cmd == ADDGRP) usage_add();
            if (cmd == REMGRP) usage_remove();
            exit(1);
        }

        break;

    case LIST:
        // -f file.fwb -o object [-r] [-Fformat_string] [-d]
        // object can be either path or ID
        while( (opt=getopt(argc, args, "f:o:crdF:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 'o': object = optarg; break;
            case 'c': list_children = true; break;
            case 'r': recursive = true; break;
            case 'F': list_format = optarg; break;
            case 'd': full_dump = true; break;
            }
        }

        if (filename.empty() || object.empty())
        {
            usage_list();
            exit(1);
        }

        break;

    case UPGRADE:
        // -f file.fwb
        autoupgrade_flag = true;
        while( (opt=getopt(argc, args, "f:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            }
        }

        if (filename.empty())
        {
            usage_upgrade();
            exit(1);
        }

        break;

    case STRUCT:
        // -f file.fwb
        while( (opt=getopt(argc, args, "f:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            }
        }

        if (filename.empty())
        {
            usage_checktree();
            exit(1);
        }

        break;
    
    case MERGE:
        // -f file1.fwb -i file2.fwb
        while( (opt=getopt(argc, args, "f:i:c:")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 'i': filemerge = optarg; break;
            case 'c': conflict_res = atoi(optarg); break;
            }
        }

        if (filename.empty() || filemerge.empty())
        {
            usage_merge();
            exit(1);
        }

        break;

    case IMPORT:
        // -f file.fwb -i config.txt -o /User/Firewalls/new_firewall
        while( (opt=getopt(argc, args, "f:i:o:d")) != EOF )
        {
            switch(opt)
            {
            case 'f': filename = optarg; break;
            case 'i': import_config = optarg; break;
            case 'o': object = optarg; break;
            case 'd': deduplicate = true; break;
            }
        }

        if (filename.empty() || import_config.empty() || object.empty())
        {
            usage_import();
            exit(1);
        }

        break;

    case NONE:
        break;
    }

    if (cmd==NONE || filename=="")
    {
        usage();
        exit(1);
    }

    init(argv);

    try 
    {
        new Resources(Constants::getResourcesFilePath());

        /* create database */
        objdb = new FWObjectDatabase();

        /* load the data file */
        UpgradePredicate upgrade_predicate(autoupgrade_flag); 

        objdb->load(filename,  &upgrade_predicate, Constants::getDTDDirectory());
    
        if (cmd == MERGE)
        {
            if (filemerge.empty())
            {
                cerr << "The name of the file that should be merged is missing"
                     << endl;
                usage_merge();
                exit(1);
            }
            mergeTree(objdb, filemerge, conflict_res);
        }

        else if (cmd == IMPORT)
        {
            if (import_config.empty() || object.empty())
            {
                cerr << "Configuration file name and path to the new firewall "
                    "object are mandatory options for import" << endl;
                usage_import();
                exit(1);
            }

            QStringList components = QString::fromUtf8(object.c_str())
                .split("/", QString::SkipEmptyParts);
            string fw_name = components.last().toUtf8().constData();
            
            Library *library = NULL;
            while (library == NULL)
            {
                components.pop_back();
                string library_path = components.join("/").toUtf8().constData();

                list<FWObject*> objects;
                findObjects(library_path, objdb, objects);

                if (objects.size() == 0)
                {
                    cerr << "Library or folder '"
                         << library_path << "' not found" << endl;
                    usage_import();
                    exit(1);
                }

                library = Library::cast(objects.front());
            }

            cout << "Import firewall configuration from file "
                 << import_config
                 << endl;

            cout << "New firewall object '"
                 << fw_name
                 << "' will be created in library '"
                 << library->getName()
                 << "'"
                 << endl;

            importConfig(import_config, library, fw_name, deduplicate);
        }

        else if (cmd == STRUCT)
        {
            checkAndRepairTree(objdb);
        }

        else if (cmd == LIST)
        {
            listObject(objdb, object, list_children, recursive,
                       list_format, full_dump);
            return(0);
        }

        else if (cmd == UPGRADE)
        {
            cout << "File upgraded; current data format version: "
                 << libfwbuilder::Constants::getDataFormatVersion() << endl;
        }

        else  if (cmd == NEWOBJECT)
        {
            newObject(objdb, objtype, name, comment_txt, parent, ops);
        }

        else  if (cmd == DELOBJECT)
        {
            delObject(objdb, object);
        }

        else  if (cmd == MODOBJECT)
        {
            modObject(objdb, object, comment_txt, ops);
        }

        else
        {

            list<FWObject*> objects;
            findObjects(object, objdb, objects);
            if (objects.size()==0)
            {
                cout << "Object " << object << " not found" << endl;
                exit(-1);
            }

            for (list<FWObject*>::iterator it=objects.begin();
                 it!=objects.end(); ++it)
            {
                FWObject *obj = *it;

                if (cmd==ADDGRP)
                {
                    list<FWObject*> groups;
                    findObjects(group, objdb, groups);
                    if (groups.size()==0)
                    {
                        cout << "Group " << group << " not found" << endl;
                        exit(-1);
                    }
                    FWObject *grp = groups.front();
                    cout << "Adding object '" << obj->getName()
                         << "' to the group '" << grp->getName()
                         << "'" << endl;
                    grp->addRef(obj);
                }
                if (cmd==REMGRP)
                {
                    list<FWObject*> groups;
                    findObjects(group, objdb, groups);
                    if (groups.size()==0)
                    {
                        cout << "Group " << group << " not found" << endl;
                        exit(-1);
                    }
                    FWObject *grp = groups.front();
                    cout << "Removing object '" << obj->getName()
                         << "' from the group '" << grp->getName()
                         << "'" << endl;
                    grp->removeRef(obj);
                }
            }
        }

        QString filename_qstr = QString::fromUtf8(filename.c_str());
        QString bakfile = filename_qstr + ".bak";

        QFile bakf(bakfile);
        if (bakf.exists()) bakf.remove();

        QFile dataf(filename_qstr);
        if (dataf.rename(bakfile))
        {
            objdb->saveFile(filename);
        } else
        {
            cout << "Could not rename data file, abroting operation" << endl;
            cout << dataf.errorString().toStdString() << endl;
            exit(-1);
        }

    } catch(FWException &ex)  {
        cerr << ex.toString() << endl;
        exit(1);
    } catch (std::string s) {
        cerr << s;
        exit(1);
    } catch (std::exception ex) {
        cerr << ex.what();
        exit(1);
    } catch (...) {
        cerr << "Unsupported exception";
        exit(1);
    }

    return(0);
}
void CompilerDriver_pix::pixNetworkZoneChecks(Firewall *fw,
                                              list<FWObject*> &all_interfaces)
{
    multimap<string, FWObject*> netzone_objects;
    Helper helper(NULL);

    for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
    {
        Interface *iface = dynamic_cast<Interface*>(*i);
        assert(iface);

        if (iface->getOptionsObject()->getBool("cluster_interface")) continue;
        if (iface->isDedicatedFailover()) continue;
        if (iface->isUnprotected()) continue;


        /*
         * in PIX, we need network zones to be defined for all
         * interfaces
         */
        string netzone_id = iface->getStr("network_zone");
        if (netzone_id=="")
        {
            QString err("Network zone definition is missing for interface '%1' (%2)");
            abort(fw, NULL, NULL,
                  err.arg(iface->getName().c_str())
                  .arg(iface->getLabel().c_str()).toStdString());
            throw FatalErrorInSingleRuleCompileMode();
        }

        FWObject *netzone = objdb->findInIndex(
            FWObjectDatabase::getIntId(netzone_id));
        if (netzone==NULL) 
        {
            QString err("Network zone points at nonexisting object for "
                        "interface '%1' (%2)");
            abort(fw, NULL, NULL,
                  err.arg(iface->getName().c_str())
                  .arg(iface->getLabel().c_str()).toStdString());
            throw FatalErrorInSingleRuleCompileMode();
        }
/*
 * netzone may be a group, in which case we need to expand it
 * (recursively). 
 * 
 * 1. We create new temporary object (type Group).
 *
 * 2. put it in the database somewhere
 *
 * 3. add all objects that belong to the network zone to this
 * group. We add objects directly, not as a reference.
 *
 * 4. finally replace reference to the old network zone object in the
 * interface with reference to this new group.
 *
 * 5. we store ID of the original network zone object 
 *    using iface->setStr("orig_netzone_id")
 *
 * This ensures netzones do not contain other groups and do not
 * require any recursive expanding anymore. Since objects were added
 * to netzones directly, we do not need to bother with dereferencing,
 * too.
 */
        list<FWObject*> ol;
        helper.expand_group_recursive(netzone, ol);

        FWObject *nz = objdb->createObjectGroup();
        assert(nz!=NULL);
        nz->setName("netzone_" + iface->getLabel());
        objdb->add(nz);

        for (list<FWObject*>::iterator j=ol.begin(); j!=ol.end(); ++j)
        {
            Address *addr = Address::cast(*j);
            if (addr == NULL || addr->getAddressPtr() == NULL)
            {
                QString err("Network zone of interface '%1' uses object '%2' "
                            "that is not an address");
                abort(fw, NULL, NULL,
                      err.arg(iface->getLabel().c_str())
                      .arg((*j)->getName().c_str()).toStdString());
                throw FatalErrorInSingleRuleCompileMode();
            }

/*
  Commented out for SF bug 3213019

  currently we do not support ipv6 with PIX/ASA and FWSM. If user
  creates a group to be used as network zone object and places ipv6
  address in it, this address should be ignored while compiling the
  policy but this should not be an error. Compiler uses network zone
  group to do various address matching operations when it tries to
  determine an interface for a rule where user did not specify
  one. Since we never (should) have ipv6 in policy and nat rules,
  compiler is not going to have anything to compare to ipv6 address in
  the network zone even if there is one and this ipv6 address is going
  to be ignored.


            if (addr->getAddressPtr()->isV6())
            {
                QString err("Network zone of interface '%1' uses object '%2' "
                            "that is IPv6 address");
                abort(fw, NULL, NULL,
                      err.arg(iface->getLabel().c_str())
                      .arg((*j)->getName().c_str()).toStdString());
                throw FatalErrorInSingleRuleCompileMode();
            }
*/
            netzone_objects.insert(
                pair<string,FWObject*>(iface->getLabel(),*j));
            nz->addRef(*j);
        }
        iface->setStr("orig_netzone_id", netzone_id );
        iface->setStr("network_zone",
                      FWObjectDatabase::getStringId(nz->getId()) );
    }


/*
 * the same object (network or host) can not belong to network zones
 * of two different interfaces. Map netzone_objects holds pairs
 * interface_id/object. We just make sure the same object does not
 * appear in two pairs with different interfaces.
 */
    multimap<string,FWObject*>::iterator k;
    for (k=netzone_objects.begin(); k!=netzone_objects.end(); ++k)
    {
        multimap<string,FWObject*>::iterator l;
        l=k;
        ++l;
        for ( ; l!=netzone_objects.end(); ++l)
        {
            if ( l->second->getId() == k->second->getId() )
            {
                if (k->first==l->first)
                {
                    QString err("Object %1 is used more than once in network "
                                "zone of interface '%2'");
                    abort(fw, NULL, NULL,
                          err.arg(l->second->getName().c_str())
                          .arg(k->first.c_str()).toStdString());
                    throw FatalErrorInSingleRuleCompileMode();
                } else
                {
                    QString err("Object %1 is used in network zones of "
                                "interfaces '%2' and '%3'");
                    abort(fw, NULL, NULL,
                          err.arg(l->second->getName().c_str())
                          .arg(k->first.c_str())
                          .arg(l->first.c_str()).toStdString());
                    throw FatalErrorInSingleRuleCompileMode();
                }
            }
        }
    }


}